092. Isinstance()
Check an object's type safely before operating on it
092. Isinstance()
Rohanβs RAM manager reads process data from psutil and builds snapshot dicts. But psutil sometimes returns unexpected types depending on the OS β an RSS value might be an int on Linux but a float on macOS. Downstream code crashes when it calls // 1024 on a float.
He adds a type guard:
def rss_to_mb(rss):
if type(rss) == int: # WRONG: rejects float, rejects bool subclass
return rss // (1024 * 1024)
raise TypeError(f"Expected int, got {type(rss).__name__}")The fix:
def rss_to_mb(rss):
if not isinstance(rss, (int, float)):
raise TypeError(f"Expected number, got {type(rss).__name__}")
return int(rss) // (1024 * 1024)Why
isinstanceovertype() ==? Two reasons:
isinstance(x, (int, float))checks both types in one call.isinstancerespects inheritance βboolis a subclass ofint, soisinstance(True, int)isTrue. This matters if you ever subclass built-ins.
He also uses isinstance to dispatch between snapshot formats:
def normalize(value):
if isinstance(value, str):
return value.strip().lower()
elif isinstance(value, (int, float)):
return round(value, 2)
elif isinstance(value, list):
return [normalize(v) for v in value]
return valueOne function. No type() == anywhere.
π‘ Fun fact: bool being a subclass of int in Python is not just a quirk β it means True + True == 2 and sum([True, False, True, True]) == 3. This is intentional and lets you count truthy values with sum(). But it also means isinstance(True, int) returns True, which is why you must check for bool before int in any isinstance dispatch chain.
β οΈ Watch out: type(x) == int is an exact type match that rejects subclasses β so it fails for bool values and any custom subclass of int. In production Python, always prefer isinstance(x, int) unless you specifically need to exclude subclasses. The PEP 8 style guide explicitly recommends isinstance() over type() comparisons.
π€ Think about it: Pythonβs philosophy of βduck typingβ says: donβt check what type something is β check what it can do. So instead of isinstance(x, (int, float)), you could try: x + 0 except TypeError: .... When would duck typing be better than isinstance, and when would explicit type checking be safer?
Learning objectives
- Use isinstance() for type checking instead of type() ==
- Check against multiple types with a tuple argument
- Understand that bool is a subclass of int
- Know when to check for bool before int in isinstance chains
Key concepts
- isinstance
- type checking
- subclass
- duck typing
Try it
Concept detail
isinstance(obj, type) β True if obj is an instance of type or any subclass. isinstance(obj, (int, float)) β True if obj is int OR float (tuple of types).
Why isinstance over type(x) == T: type(x) == int is an exact match β rejects subclasses, must list types separately. isinstance(x, int) accepts int and all subclasses (like bool).
bool is a subclass of int: isinstance(True, int) β True type(True) == int β False (type is bool, not int) Check bool BEFORE int if you want to distinguish them.
Type hierarchy example: bool β int β object isinstance(True, int) β True (bool inherits from int) isinstance(True, object) β True (everything inherits from object)
For duck typing, prefer try/except over isinstance when possible: try: result = x + 1 except TypeError: β¦ # x is not numeric This works even for custom classes that behave like numbers.
type(x).name gives the type name as a string for error messages.
Solution
import math
def safe_sqrt(x):
if not isinstance(x, (int, float)):
raise TypeError(f"Expected number, got {type(x).__name__}")
return math.sqrt(x)
def describe(value):
if isinstance(value, bool):
return "unknown"
elif isinstance(value, int):
return "integer"
elif isinstance(value, float):
return "float"
elif isinstance(value, str):
return "string"
elif isinstance(value, list):
return "list"
return "unknown"
def sum_numbers(items):
return sum(x for x in items if isinstance(x, (int, float)) and not isinstance(x, bool))Tests
def test_safe_sqrt_int():
assert abs(safe_sqrt(4) - 2.0) < 0.001
def test_safe_sqrt_float():
assert abs(safe_sqrt(2.0) - 1.4142) < 0.001
def test_safe_sqrt_type_error():
with pytest.raises(TypeError):
safe_sqrt("four")
def test_describe_int():
assert describe(42) == "integer"
def test_describe_float():
assert describe(3.14) == "float"
def test_describe_str():
assert describe("hi") == "string"
def test_sum_numbers():
assert sum_numbers([1, "a", 2.5, None, 3]) == 6.5