Python is dynamically typed: a variable can hold different kinds of values during a program's run, and you do not declare a fixed type when you assign it.
To check type in Python at runtime, use type() or isinstance(). Use type(x) to see the exact type of a variable. Use isinstance(x, int) when you want to check whether a variable is an instance of a type or subclass. Python's official docs define isinstance() as returning True when an object is an instance of a class, subclass, tuple of classes, or union type.
Type hints are different: they help editors and tools such as mypy analyze code, but Python does not enforce them automatically at runtime.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
Python check type quick reference
| Task | Use |
|---|---|
| Print the exact type of a variable | type(variable) |
| Check if a value is an int | isinstance(value, int) |
| Check if a value is a string | isinstance(value, str) |
| Check multiple possible types | isinstance(value, (int, float)) |
| Check exact type only | type(value) is int |
| Check class or subclass | isinstance(obj, ClassName) |
| Check if a class is a subclass | issubclass(Child, Parent) |
| Add static type information | type hints |
| Run static type checking | mypy or another type checker |
Check the type of a variable using type()
The built-in type() function returns the type object for any value. It is useful for debugging and quick inspection.
x = 42
print(type(x))
print(type("hello"))
print(type([1, 2, 3]))
print(type({"a": 1}))
print(type((1, 2)))
print(type(True))Each line prints a <class '…'> line for int, str, list, dict, tuple, and bool respectively.
Use type() when you want to know exactly what Python sees at runtime—for example, while debugging unexpected data in a variable.
To require an exact type (not a subclass), compare with is:
values = [10, True, 3.14]
for value in values:
if type(value) is int:
print(f"{value!r} is exactly int")
else:
print(f"{value!r} is not exactly int")Only 10 is reported as exactly int. True is not, even though it behaves like 1 in many contexts—more on that in the common types section below.
Check if a variable is a specific type using isinstance()
isinstance() returns True or False. It is usually the better choice for real type checks inside if statements because it understands inheritance and accepts a tuple of types.
name = "Alice"
count = 7
items = [1, 2, 3]
print(isinstance(name, str))
print(isinstance(count, int))
print(isinstance(items, list))
print(isinstance(items, (list, tuple)))All four checks print True.
Check if a value is a string
def greet(value):
if isinstance(value, str):
return f"Hello, {value}"
return "Expected a string"
print(greet("Sam"))
print(greet(42))The first call returns a greeting; the second returns "Expected a string".
Check if a value is a number
def double_if_number(value):
if isinstance(value, (int, float)):
return value * 2
return None
print(double_if_number(5))
print(double_if_number(2.5))
print(double_if_number("5"))You get 10, 5.0, and None for integer, float, and non-numeric input.
Check if data is a list or tuple
def first_item(data):
if isinstance(data, (list, tuple)):
return data[0]
raise TypeError("Expected a list or tuple")
print(first_item([10, 20, 30]))
print(first_item((1, 2, 3)))Both calls return 10 and 1.
Inheritance-aware checks
isinstance() follows the class hierarchy:
class Vehicle:
pass
class Car(Vehicle):
pass
my_car = Car()
print(isinstance(my_car, Car))
print(isinstance(my_car, Vehicle))
print(type(my_car) is Vehicle)The first two lines print True; the third prints False. isinstance(my_car, Vehicle) is True because Car is a subclass of Vehicle, but type(my_car) is Vehicle is False because the exact type is Car.
type() vs isinstance()
| Feature | type() | isinstance() |
|---|---|---|
| Returns | Type object | True or False |
| Checks exact type | Yes | No; includes subclasses |
| Good for debugging | Yes | Sometimes |
| Good for conditional checks | Sometimes | Yes |
| Supports multiple types directly | No | Yes, with a tuple |
| Handles inheritance | No | Yes |
Recommendation: use type() when you want to inspect the exact type. Use isinstance() when you want to check whether a value can be treated as a certain type—especially in functions that accept flexible input.
Check multiple types in Python
Pass a tuple as the second argument to isinstance():
def normalize_length(value):
if isinstance(value, (str, list, tuple)):
return len(value)
raise TypeError("Expected str, list, or tuple")
print(normalize_length("hello"))
print(normalize_length([1, 2, 3, 4]))The function returns 5 for the string and 4 for the list.
This pattern is common when a function accepts more than one type—for example, numbers (int and float), sequences (list and tuple), or string-or-list inputs.
Check common Python data types
| Data type | Check |
|---|---|
| String | isinstance(x, str) |
| Integer | isinstance(x, int) |
| Float | isinstance(x, float) |
| List | isinstance(x, list) |
| Tuple | isinstance(x, tuple) |
| Dictionary | isinstance(x, dict) |
| Set | isinstance(x, set) |
| Boolean | isinstance(x, bool) |
| None | x is None |
Quick examples:
samples = {
"text": "go",
"number": 99,
"ratio": 0.5,
"items": [1, 2],
"pair": (1, 2),
"mapping": {"k": "v"},
"unique": {1, 2},
"flag": False,
"empty": None,
}
checks = [
("text", isinstance(samples["text"], str)),
("number", isinstance(samples["number"], int)),
("ratio", isinstance(samples["ratio"], float)),
("items", isinstance(samples["items"], list)),
("pair", isinstance(samples["pair"], tuple)),
("mapping", isinstance(samples["mapping"], dict)),
("unique", isinstance(samples["unique"], set)),
("flag", isinstance(samples["flag"], bool)),
("empty", samples["empty"] is None),
]
for name, result in checks:
print(f"{name}: {result}")Every check prints True.
Important: bool is a subclass of int in Python, so isinstance(True, int) returns True. If you need to distinguish booleans from integers, check bool first or use type(x) is bool:
print(isinstance(True, int))
print(isinstance(True, bool))
print(type(True) is int)The first two lines print True; the third prints False.
Check for None in Python
Use identity comparison:
value = None
if value is None:
print("value is None")Do not use:
type(x) is None—type(None)is<class 'NoneType'>, notNoneisinstance(x, None)—Noneis not a valid class forisinstance()
For optional values, x is None and x is not None are the standard patterns.
Check custom class type
For your own classes, isinstance() works with parent and child classes:
class Animal:
pass
class Dog(Animal):
pass
pet = Dog()
print(isinstance(pet, Dog))
print(isinstance(pet, Animal))
print(type(pet) is Animal)The first two lines print True; the third prints False. Use type(obj) is ClassName only when you specifically want to reject subclasses and match the exact class.
To check relationships between classes themselves, use issubclass():
print(issubclass(Dog, Animal))
print(issubclass(Animal, Dog))The first line prints True; the second prints False.
Check if an object is callable
When you only need to know whether something can be called, use callable():
print(callable(print))
print(callable(len))
print(callable(42))
print(callable(lambda x: x * 2))print, len, and the lambda return True; the integer 42 returns False.
Do not overuse exact type checks to detect functions. In Python, behavior often matters more than the exact class name.
Runtime type checking vs static type checking
| Approach | When it runs | Examples |
|---|---|---|
| Runtime checks | While the program executes | type(), isinstance(), callable() |
| Static checks | Before or during development | type hints analyzed by mypy, Pyright, or your IDE |
type() and isinstance() inspect live objects. Type hints are annotations for developers and tools—they do not stop Python from running code with mismatched values unless you add explicit runtime checks yourself.
Install and run mypy on a file when you want static analysis:
pip install mypy
mypy script.pyThis is not a full mypy tutorial; the key point is that static checkers complement—not replace—runtime checks.
Type hints in Python
Type hints document expected types without enforcing them at runtime:
def add(x: int, y: int) -> int:
return x + y
name: str = "Alice"
scores: list[int] = [90, 85, 88]
profile: dict[str, int] = {"age": 30}
label: str | None = NoneModern Python uses built-in generics such as list[int] and union syntax such as str | None (Python 3.10+). These help readability and static tools; they are not a substitute for isinstance() when you need runtime validation of user input.
For return types and more complex signatures, annotations follow the same idea: document intent, then let a type checker verify consistency before runtime.
When not to check types
Python often prefers duck typing: if an object behaves the way you need, its exact type may not matter.
Alternatives to heavy type checking:
- Use
try/exceptwhen converting input instead of longisinstance()chains. - Use
callable()when you need something invokable. - Use
hasattr()carefully when probing for capabilities—prefer well-defined interfaces when possible.
Example with conversion instead of pre-checking every type:
def to_int(value):
try:
return int(value)
except (TypeError, ValueError):
return None
print(to_int("42"))
print(to_int("abc"))to_int("42") returns 42; invalid input returns None.
Flexible code that handles behavior gracefully is often more Pythonic than strict type gates everywhere.
Summary
Use type(x) to inspect the exact type of a variable at runtime. For most practical checks in conditions, prefer isinstance(x, SomeType), and pass a tuple such as (int, float) when more than one type is valid. Test for None with x is None, not with type(). Type hints and tools like mypy help during development but do not enforce types when the program runs. When strict type gates add noise without real safety, lean on duck typing, callable(), or try / except instead.
Useful references
- Python built-in functions:
type(),isinstance(),issubclass() - PEP 484 — Type Hints
- mypy — Static Type Checker

