Skip to content

Python Core Data Types & Type Checking

Every value in Python has a type that determines what it can do. Python is dynamically typed Runtime: it works out a value’s type automatically when the code runs, and a variable can be reassigned to a value of a different type at any time.

Python is also strongly typed. It will not silently convert between unrelated types, so '5' + 5 is an error rather than 10 or '55'.

TaskToolExample
Find a value’s exact typetype(x)type('hi') gives <class 'str'>
Check a value against a typeisinstance(x, T)isinstance(5, int) gives True
Check against several typesisinstance(x, (A, B))isinstance(5, (int, float)) gives True
Convert between typesint(), float(), str(), bool()int('42') gives 42

A variable is just a name attached to a value. The type belongs to the value, not the name, so the name can be reattached to a value of any type.

dynamic_typing.py
age = 25 # int
age = 'Twenty-five' # Now a str, which is perfectly legal

TypeNameExampleNotes
intInteger42, -7, 1_000_000Whole numbers with unlimited size. Underscores are allowed for readability.
floatFloat4.5, -0.01, 1e6Numbers with a decimal point. Stored as approximations.
strString'hello', "hello"Text in single or double quotes. Immutable.
boolBooleanTrue, FalseLogical values. A subclass of int.
NoneTypeNoneNoneRepresents “no value”. There is only one None.
core_types.py
my_integer = 10 # <class 'int'>
my_float = 4.50 # <class 'float'>
my_string = 'hello' # <class 'str'>
my_boolean = True # <class 'bool'>
my_none = None # <class 'NoneType'>
numeric_behavior.py
print(10 / 2) # 5.0, the / operator always returns a float
print(10 // 3) # 3, floor division returns an int for int operands
print(2 ** 100) # 1267650600228229401496703205376, ints never overflow
print(1 + 2.0) # 3.0, int and float mix, and the result is a float
print(0.1 + 0.2) # 0.30000000000000004, floats are approximations

Collections hold multiple values. They are covered in their own guides, so this is just a preview.

TypeExampleMutableOrdered
list[1, 2, 3]YesYes
tuple(1, 2, 3)NoYes
dict{'a': 1}YesYes (insertion order)
set{1, 2, 3}YesNo

Python promotes numbers automatically (int with float gives float), but it refuses to combine unrelated types.

type_errors.py
print('5' * 3) # '555', repeating a string is allowed
print('5' + 5) # TypeError: can only concatenate str (not "int") to str
print(5 + '5') # TypeError: unsupported operand type(s) for +: 'int' and 'str'
print('10' / 2) # TypeError: unsupported operand type(s) for /: 'str' and 'int'

Convert a value explicitly by calling the target type as a function.

CallResultNotes
int('42')42The string must look like a whole number
int(3.9)3Truncates toward zero, it does not round
float('4.5')4.5Accepts integer or decimal text
str(10)'10'Works on almost any value
bool('False')TrueAny non-empty string is truthy
int('3.5')ValueErrorConvert through float first: int(float('3.5'))
int('abc')ValueErrorNot a valid number

type(value) returns the exact class of a value.

type_function.py
developer = 'Devin'
print(type(developer)) # <class 'str'>
print(type(developer).__name__) # str, handy for readable messages
print(type(developer) is str) # True

isinstance(value, type_or_tuple) returns True if the value matches the type, or any type in a tuple.

isinstance_function.py
account_balance = '12'
print(isinstance(account_balance, int)) # False
print(isinstance(account_balance, (int, float))) # False, it is a string
print(isinstance(account_balance, str)) # True
type(x) is Tisinstance(x, T)
MatchesThe exact type onlyThe type and any type built on top of it
Multiple typesNeeds orAccepts a tuple
Typical useStrict checks, debuggingGeneral validation

The difference shows up with booleans, which are built on top of integers:

type_vs_isinstance.py
print(type(True) is int) # False, the exact type is bool
print(isinstance(True, int)) # True, bool is built on top of int

In Python, bool is built on top of int. As a result, True and False pass an integer check.

boolean_trap.py
print(isinstance(True, int)) # True, a surprise
print(True + True) # 2, booleans behave like 1 and 0
print(type(True) is int) # False, the exact type is bool

This causes real bugs. Code expecting a quantity will happily accept True as 1. To reject booleans when you need an integer, use either approach:

reject_bool.py
value = True
# Option 1: allow int, but explicitly exclude bool
print(isinstance(value, int) and not isinstance(value, bool)) # False
# Option 2: exact type match
print(type(value) is int) # False

  1. Identify the type. Use type() to discover what a variable currently holds, especially while debugging.

  2. Validate the input. Run isinstance() to confirm the value is safe to process.

  3. Filter out booleans if needed. Remember that True and False pass integer and numeric checks.

  4. Execute safely. Run math or text operations only after the value has passed validation.

validate_then_operate.py
account_balance = '12'
if isinstance(account_balance, (int, float)) and not isinstance(account_balance, bool):
print(account_balance / 2)
else:
print('Error: Operation rejected. Balance is not numeric.') # Runs

| isinstance(x, int) to reject booleans | True and False pass | Use type(x) is int |