Python by Example

Values

Python's built-in scalar types: int, float, str, bool, None, and bytes - with identity versus equality.

Python has six built-in scalar types: int, float, str, bool, None, and bytes. Everything in Python is an object, so every value carries its type with it. type(x) returns the object's class; isinstance(x, SomeType) tests membership, including subclasses.

Each scalar type has a literal form. type() returns the class of any value.

type(42)        # <class 'int'>
type(3.14)      # <class 'float'>
type("hi")      # <class 'str'>
type(True)      # <class 'bool'>
type(None)      # <class 'NoneType'>
type(b"bytes")  # <class 'bytes'>

isinstance(x, T) is the idiomatic way to check a value's type at runtime because it respects inheritance. bool is a subclass of int, so isinstance(True, int) is True. This can produce surprises if you expect the check to be strict.

isinstance(42, int)      # True
isinstance(3.14, float)  # True
isinstance("hi", str)    # True
 
# bool is a subclass of int
isinstance(True, int)    # True  <- not False
isinstance(True, bool)   # True

== compares values; is compares identity (whether two names point to the exact same object in memory). Use == for value comparison and is only for None, True, False, and sentinel singletons.

1 == 1.0    # True  - same value, different types
1 is 1.0    # False - different objects
 
x = None
x is None   # True  - canonical None check
x == None   # True  - works, but non-idiomatic

In production

bool is a subclass of int (True == 1, isinstance(True, int) is True) and this leaks into JSON serialisation, sum(), and dict keys - a function expecting an integer silently accepts True. Use is only for None, True, False, and sentinel singletons; use == for value comparison everywhere else.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free