Data Types in Python: A Beginner's Guide
Part of the series Python Basics — part 1
Every value in Python has a data type, and the type determines what you can do with that value. You can add two numbers, but you can’t “add” a number to a word in the same way.
In this blog, we’ll learn about the most important built-in data types in Python and see how each one behaves.
What is a data type?
A data type tells Python what kind of value an object represents, and therefore which operations make sense for it.
Think of a spreadsheet: a cell can hold a number, a piece of text, or a TRUE/FALSE value, and each one behaves differently when you use it in a formula. Python works in a similar way.
You can ask Python for the type of any value with the built-in type() function:
print(type(42))
print(type("hello"))
Output:
<class 'int'>
<class 'str'>
Python’s built-in data types at a glance
Python has several built-in types that are commonly used to represent numbers, text, collections, Boolean values, and binary data. Here are the main ones we’ll cover in this post.
| Category | Types | Mutability? | Example |
|---|---|---|---|
| Numeric | int, float, complex | No | 42, 3.14, 2 + 3j |
| Text | str | No | "Hello" |
| Boolean | bool | No | True |
| None | NoneType | No | None |
| Sequence | list, tuple, range | list: yes. tuple and range: no | [1, 2, 3], (1, 2, 3), range(6) |
| Mapping | dict | Yes | {"name": "Alice"} |
| Set | set, frozenset | set: yes. frozenset: no | {1, 2, 3} |
| Binary | bytes, bytearray, memoryview | bytes: no. bytearray: yes. memoryview: depends on what it views | b"Hello" |
A mutable value can be modified in place after it’s created. An immutable value cannot be changed in place; any apparent “change” creates a new value instead.
list, tuple, set, and dict are built-in collection types. They are commonly used as Python’s core data structures for storing groups of values.
To understand these data structures in more detail, read this blog here: Core Data Structures in Python
Numeric types
Python has three built-in numeric types: int, float and complex.
Integers (int)
An int is a whole number, positive or negative, with no decimal point. Python integers can grow very large, limited mainly by the memory available to the program.
students = 30
new_students = 5
total = students + new_students
print(total) # 35
print(type(total)) # <class 'int'>
Python has two kinds of division, and the difference matters:
print(7 / 2) # 3.5 (normal division, always gives a float)
print(7 // 2) # 3 (floor division, drops the decimal part)
print(7 % 2) # 1 (the remainder)
Note: floor division doesn’t simply “drop the decimal part” in every case. With positive numbers such as 7 // 2, the result is 3, but with negative numbers, it rounds down toward negative infinity.
Floats (float)
A float is a floating-point number. You will often see floats written with a decimal point:
temperature = 36.6
price = 19.99
print(type(temperature)) # <class 'float'>
print(price * 2) # 39.98
Floats are stored using binary floating-point representation, which can produce tiny rounding errors.
print(0.1 + 0.2) # 0.30000000000000004
This isn’t a Python bug. Some decimal fractions cannot be represented exactly using binary floating-point numbers.
When you need a rounded result for display, you can use round():
print(round(0.1 + 0.2, 2)) # 0.3
Complex numbers (complex)
A complex number has two parts: a real part and an imaginary part. In maths, the imaginary unit is usually written as i. Python uses j instead.
z = 2 + 3j # real part 2, imaginary part 3
print(type(z)) # <class 'complex'>
print(z.real) # 2.0
print(z.imag) # 3.0
print((2 + 3j) + (1 + 2j)) # (3+5j) (add the real parts and the imaginary parts)
print(1j * 1j) # (-1+0j) (j squared is -1)
print(abs(3 + 4j)) # 5.0 (the size, or magnitude, of the number)
Complex numbers are commonly used in areas such as science and engineering, including signal processing and other mathematical applications.
You may not use them if science, engineering, or mathematical computing isn’t part of what you’re learning Python for.
So, you can skim this section for now. It’s still worth knowing that the type exists because it is one of Python’s built-in numeric types.
Text type
Strings (str)
A str (string) is text. You create one by wrapping characters in single or double quotes.
course = "Intro to Python"
level = 'Beginner'
print(course) # Intro to Python
There are several ways to represent text that spans multiple lines. One option is triple quotes (""" or '''):
message = """Dear student,
Welcome to the course."""
print(message)
Another option is the \n character, which means “start a new line”. Put it inside a normal string wherever you want a line break:
message = "Dear student,\nWelcome to the course."
print(message)
Both versions print the same thing:
Dear student,
Welcome to the course.
Use \n\n (two in a row) for a paragraph break, which leaves a blank line between the two parts:
message = "Dear student,\n\nWelcome to the course.\nWe start on Monday."
print(message)
Dear student,
Welcome to the course.
We start on Monday.
Behind the scenes, the line breaks represented by a triple-quoted string can be represented as newline characters (\n). That’s why print(repr(message)) can show \n where the lines break.
Python has no separate “character” type. A single character is just a string with a length of 1:
print(type("a")) # <class 'str'>
print(len("a")) # 1
Strings come with plenty of built-in tools, called string methods. They are all explained, with examples, in String Methods in Python. Here are a few:
print(len(course)) # 15
print(course.upper()) # INTRO TO PYTHON
print(course[0]) # I (the first character)
You can build a string from other values with an f-string, which is a string that starts with f and puts values inside curly braces:
students = 30
print(f"{course} has {students} students at {level} level.")
# Intro to Python has 30 students at Beginner level.
The + and * operators work on strings too, but they do different jobs than they do on numbers: + joins two strings together, and * repeats a string. Python Coding Exercise #54 is all about this.
print("2" + "5") # 25
print("5" * 3) # 555
This is exactly why the program in Python Coding Exercise #01 prints 2555. Python’s input() function always returns a string, even when the user types a number.
Strings are immutable: you can’t change a character in place.
name = "Python"
name[0] = "J"
TypeError: 'str' object does not support item assignment
Instead, you create a new string. The original stays the same:
name = "Python"
new_name = name.replace("P", "J")
print(new_name) # Jython
print(name) # Python
Boolean type
Booleans (bool)
A bool has only two possible values: True and False. Both are written with a capital first letter.
is_student = True
has_passed = False
print(type(is_student)) # <class 'bool'>
Booleans often come from comparisons:
students = 30
print(students > 20) # True
print(students == 50) # False
They are what if statements use to make decisions. Notice that == (two equals signs) asks “are these equal?”, while a single = assigns a value to a variable.
The None type
None (NoneType)
None is Python’s way of representing the absence of a value. It is the only value of type NoneType.
exam_score = None
print(exam_score) # None
print(type(exam_score)) # <class 'NoneType'>
It’s useful as a placeholder, for example when a student hasn’t taken the exam yet, so there is no score to store. It’s also what a function returns when it doesn’t explicitly return another value.
Sequence types
A sequence is an ordered collection where every item has a position, counting from 0. Python has three built-in sequence types besides str (which is a sequence of characters): list, tuple and range.
Lists (list)
A list is an ordered collection you can change. Its items can even be of different types.
student = ["Anna", 21, True]
student.append("Computer Science")
print(student) # ['Anna', 21, True, 'Computer Science']
print(student[0]) # Anna
print(type(student)) # <class 'list'>
Tuples (tuple)
A tuple is an ordered collection that can’t be changed after it is created. It’s written with round brackets, and it’s handy for data that shouldn’t be altered by accident.
coordinates = (4, 5)
print(coordinates[0]) # 4
print(type(coordinates)) # <class 'tuple'>
A tuple with a single item needs a trailing comma. Otherwise Python just sees brackets around a value:
print(type((5,))) # <class 'tuple'>
print(type((5))) # <class 'int'>
Ranges (range)
A range represents a sequence of numbers. It’s most often used to repeat something a set number of times in a for loop.
for i in range(3):
print(i) # prints 0, then 1, then 2
print(range(6)) # range(0, 6)
print(list(range(6))) # [0, 1, 2, 3, 4, 5]
print(list(range(2, 10, 2)))# [2, 4, 6, 8] (start, stop, step)
Two details to remember: a range starts at 0 by default, and it stops just before the number you give it.
A range object doesn’t store every number in memory. It represents the sequence and generates values as needed, which makes even range(1000000) relatively lightweight.
Mapping type
Dictionaries (dict)
A dict (dictionary) stores key-value pairs, so you look values up using keys instead of positions. It is Python’s built-in mapping type.
student = {"name": "Anna", "age": 21, "course": "Computer Science"}
print(student["name"]) # Anna
print(type(student)) # <class 'dict'>
Dictionary keys must be hashable. Strings and numbers are common examples. Tuples can also be keys if all of their elements are hashable. Lists, dictionaries, and sets cannot be used as dictionary keys because they are mutable and unhashable.
The values in a dictionary can be of any type.
Set types
Sets (set)
A set is an unordered collection of unique items. Duplicates are automatically removed.
colors = {"red", "blue", "red", "blue", "green"}
print(len(colors)) # 3
Because a set doesn’t record element positions, you can’t get an item by index:
print(colors[0])
TypeError: 'set' object is not subscriptable
Sets are useful when you care about membership or want to remove duplicate values from a collection.
Frozen sets (frozenset)
A frozenset is an immutable set, so you can’t change its contents after it is created. Because a frozenset is immutable and hashable, it can also be used as a dictionary key, provided its elements are hashable.
fixed = frozenset({"red", "blue"})
fixed.add("green")
This produces an AttributeError because frozenset doesn’t have an add() method.
AttributeError: 'frozenset' object has no attribute 'add'
Binary types
Binary types hold raw bytes, the numbers from 0 to 255 that computers use to store files, images and data sent over a network. Python’s built-in binary sequence types include bytes, bytearray, and memoryview.
As a beginner, you’ll mainly encounter them when working with files, encodings, or other data that isn’t plain text.
Bytes (bytes)
bytes is an immutable sequence of bytes. A bytes literal looks like a string with a b in front.
data = b"Hello"
print(type(data)) # <class 'bytes'>
print(data[0]) # 72 (each item is a number, not a letter)
You can turn text into bytes with .encode() and turn bytes back into text with .decode(). They are not always the same length, because some characters need more than one byte:
text = "café"
data = text.encode("utf-8")
print(data) # b'caf\xc3\xa9'
print(len(text)) # 4
print(len(data)) # 5
print(data.decode("utf-8")) # café
Byte arrays (bytearray)
A bytearray is the mutable version of bytes, so you can change its contents in place.
buffer = bytearray(b"Hello")
buffer[0] = 74 # 74 is the code for "J"
print(buffer) # bytearray(b'Jello')
Memory views (memoryview)
A memoryview lets you access the data of an underlying bytes-like object without making a copy. That is useful for very large data, and it’s an advanced tool you can safely skip for now.
view = memoryview(b"Hello")
print(view[0]) # 72
print(bytes(view[1:3])) # b'el'
Whether you can modify data through a memoryview depends on whether the underlying object supports writing. A view of bytes is read-only, while a view of a writable object such as bytearray can be used to modify that underlying data.
Checking a type with type() and isinstance()
You’ve already seen type(). A second tool, isinstance(), answers a yes/no question: “is this value an instance of that type?”
price = 19.99
print(type(price)) # <class 'float'>
print(isinstance(price, float)) # True
print(isinstance(price, int)) # False
When you’re debugging and a result looks strange, print(type(x)) is one of the fastest ways to find out what you’re actually working with. For checking whether something is a particular type, isinstance() is generally more useful because it also takes subclasses into account.
Python is dynamically typed, but not carelessly
In Python you don’t declare a variable’s type. The type belongs to the object or value, and a variable can later refer to an object of a different type.
x = 10 # x is an int
x = "hello" # now x is a str
That flexibility can make Python feel relaxed, but Python is still strict about mixing incompatible types. It won’t guess what you meant:
print("5" + 5)
This produces a TypeError because Python doesn’t automatically concatenate a string and an integer.
TypeError: can only concatenate str (not "int") to str
Tip: Reading these error messages carefully is a skill worth building early. The above one says exactly what went wrong.
Converting between types
You can convert a value from one type to another with functions named after the types: int(), float(), str(), bool(), list(), tuple() and set(). These are Python’s built-in functions, and each one is explained in detail, with examples, in Built-in Functions in Python.
print(int("42")) # 42
print(float("3.5")) # 3.5
print(str(100)) # 100 (now it's text)
This is how you fix the 2555 problem. Convert what input() returns into numbers before doing any maths (see also Python Coding Exercise #08):
n1 = int(input("Enter first number: "))
n2 = int(input("Enter second number: "))
print(n1 + n2 * 3) # with 2 and 5, this prints 17
Two things to watch out for (the first one is the subject of Python Coding Exercise #31):
print(int(3.9)) # 3 (int() cuts off the decimal, it does not round)
print(int("abc")) # ValueError: invalid literal for int() with base 10: 'abc'
int() can convert text that represents a valid integer, but text such as "abc" cannot be converted to an integer and raises a ValueError.
The collection types can also be converted between each other, and set() is a quick way to remove duplicates:
print(list("abc")) # ['a', 'b', 'c']
print(tuple([1, 2, 3])) # (1, 2, 3)
print(sorted(set([3, 1, 3, 2]))) # [1, 2, 3]
Truthy and falsy values
bool() converts a value to True or False. Values such as 0, "", [], and None are considered False, while non-empty collections and most other values are considered True.
print(bool(0)) # False
print(bool("")) # False
print(bool([])) # False
print(bool(None)) # False
print(bool(7)) # True
print(bool("text")) # True
print(bool([0])) # True (the list isn't empty)
Mutable vs immutable
Now that you’ve seen the main built-in types, here is the recap that ties the table together.
- Immutable:
int,float,complex,bool,str,tuple,range,frozenset,bytes, andNoneTypeobjects cannot be changed in place. - Mutable:
list,dict,set, andbytearraycan be changed in place.
Mutability determines whether an object can be changed in place. It also affects whether an object can be used as a dictionary key, because dictionary keys need to be hashable. Immutability and hashability are related, but they are not exactly the same thing: for example, a tuple is immutable, but it is only hashable when all of its elements are hashable.
scores = {}
scores[("anna", 2026)] = 90 # a tuple works as a key
scores[["anna", 2026]] = 90 # a list does not
TypeError: cannot use 'list' as a dict key (unhashable type: 'list')
Older versions of Python word this error as just unhashable type: 'list', but the meaning is the same.
Key takeaways
- Every value in Python has a type, and the type determines which operations work on it.
- Python’s commonly used built-in types include numeric (
int,float,complex), text (str), boolean (bool), None (NoneType), sequence (list,tuple,range), mapping (dict), set (set,frozenset), and binary (bytes,bytearray,memoryview) types. - A mutable type can be changed in place (
list,dict,set,bytearray). An immutable type can’t. - Use
type()to see a value’s type andisinstance()to test whether a value is an instance of a particular type. - Python is dynamically typed: variables can refer to objects of different types at different points in a program, but incompatible types cannot always be mixed in the same operation.
- Convert between types with functions such as
int(),float(),str(),bool(),list(),tuple(), andset(). - Floats can have tiny rounding errors because of how floating-point numbers are represented. Use
round()when you need a rounded result for display.
✦ Thanks for reading! ✦