Every value in Python is an object, and every object has a type. Five built-in types cover the overwhelming majority of data you will handle at the start: integers (int), floating-point numbers (float), Booleans (bool), text (str) and the absence of a value (NoneType, whose single instance is None). Knowing which one you are holding — and how to convert between them safely — eliminates a huge class of runtime errors.
Python integers are arbitrary precision: 2 ** 100 is a perfectly normal value with no overflow. Floats are standard 64-bit IEEE-754 doubles, so they have about 15–17 significant digits of precision and all the usual rounding surprises (0.1 + 0.2 == 0.3 is False). When exact decimals matter — money, measurements — use decimal.Decimal instead. When fractions are needed exactly, use fractions.Fraction.
bool is technically a subclass of int, with True equal to 1 and False equal to 0. That is why sum([True, True, False]) returns 2, which is occasionally useful for counting matches. More importantly, every object has a truth value: empty containers, zero, None and empty strings are all falsy; everything else is truthy. This is what makes if items: idiomatic in place of if len(items) > 0:.
str is an immutable sequence of Unicode characters. That one word, immutable, has two important consequences: string methods never change the original (they return a new string), and strings can be used as dictionary keys. Every letter, digit, emoji, Chinese character or mathematical symbol is valid in a Python string because the default encoding for source files and string literals is UTF-8.
Finally, None is Python's way of saying "no value". It is the default return of a function with no explicit return, a common default argument to signal "use the built-in default", and the unambiguous "nothing here" for missing fields. Compare with is None and is not None, never with == None, because is checks object identity and avoids surprises with objects that define their own __eq__.
Checking and converting types
type(value) returns the exact class; isinstance(value, cls) accepts subclasses (important for booleans and custom classes). Conversion functions are the classes themselves: int("42"), float("3.14"), str(42), bool(0). All conversions are explicit; Python never silently mixes numeric and text types.
Beware that bool("False") returns True because the string is non-empty. To parse the text "True" / "False" into a boolean, compare against a whitelist or use ast.literal_eval.
Type hints, for humans and tools
Modern Python allows annotations such as def area(radius: float) -> float:. These annotations are not enforced at runtime; they are documentation that tools (mypy, pyright, your editor) use to catch mistakes before you run the code. Start simple: annotate the parameters and return types of every function that is not trivial, and let the tooling do the rest.
These are the built-in types and their conversion helpers. Click through to the docs for the full list of methods on each.
| Tool | Purpose |
|---|---|
int()built-in | Integer with arbitrary precision; converts from str or float. |
float()built-in | 64-bit IEEE-754 floating-point number. |
bool()built-in | Truth value; True and False are the only instances. |
strbuilt-in type | Immutable Unicode text; supports rich formatting and search methods. |
Noneconstant | The single instance of NoneType; means "no value". |
type()built-in | Returns the exact class of its argument. |
isinstance()built-in | Tests membership in a class or tuple of classes. |
decimal.Decimalclass | Exact decimal arithmetic for money and measurements. |
Exploring Basic Data Types code example
The example creates one value of each basic type, checks its class, and demonstrates safe conversions.
# Lesson: Exploring Basic Data Types
# Goal: observe how each built-in type behaves and convert between them safely.
from decimal import Decimal
def describe(value: object) -> str:
'''Return one line: type, repr, truthiness.'''
return f"{type(value).__name__:<5} {value!r:<25} truthy={bool(value)}"
samples: list[object] = [
42, # int
3.14, # float
True, # bool (note: also counts as int)
"Ada", # str
None, # NoneType
"", # empty str is falsy
0, # zero int is falsy
Decimal("0.10"), # exact decimal
]
for item in samples:
print(describe(item))
# Safe conversions: always guard str -> number with try/except
for text in ["10", "3.2", "nope"]:
try:
print(f"int({text!r}) = {int(text)}")
except ValueError as err:
print(f"int({text!r}) -> {err}")
# Correct comparison with None
result: int | None = None
if result is None:
print("no result yet")
Points to notice while reading:
1) describe() prints the type, repr and truthiness in one row.
2) Decimal keeps trailing zeros; float would not.
3) int("3.2") raises because int() does not parse decimals.
4) `result is None` is the idiomatic way to check for the sentinel.
Two practice snippets that isolate truthiness and the int/float boundary.
# Example A: truthiness as a filter
values = [0, 1, "", "hi", None, [], [1]]
real = [v for v in values if v]
print(real) # -> [1, 'hi', [1]]
# Example B: int vs float division
a = 7
b = 2
print(a / b) # true division -> 3.5 (always float)
print(a // b) # floor division -> 3 (int result for int inputs)
print(a % b) # modulus -> 1
print(type(a / b).__name__, type(a // b).__name__)
These assertions verify the behaviours that first trip most learners up.
assert isinstance(True, int) # bool is a subclass of int
assert bool("False") is True # any non-empty string is truthy
assert 0.1 + 0.2 != 0.3 # classic float surprise
assert Decimal("0.1") + Decimal("0.2") == Decimal("0.3")
Running the script prints one descriptive line per value:
int 42 truthy=True
float 3.14 truthy=True
bool True truthy=True
str 'Ada' truthy=True
NoneType None truthy=False
str '' truthy=False
int 0 truthy=False
Decimal Decimal('0.10') truthy=True
int('10') = 10
int('3.2') -> invalid literal for int() with base 10: '3.2'
int('nope') -> invalid literal for int() with base 10: 'nope'
no result yet