5.33. Introspection

A handful of built-ins let a running program inspect itself – the values it is working with, the namespaces those values live in, and the relationships between classes. Reach for them when you need to make decisions based on what an object actually is, rather than what its caller claims it is.

5.33.1. Identity and hashing

  • id() – a unique integer that identifies an object for as long as it is alive. Two names that bind the same object return the same id; two equal-but-distinct objects do not.

>>> a = [1, 2, 3]
>>> b = a
>>> c = [1, 2, 3]
>>> id(a) == id(b)        # same list
True
>>> id(a) == id(c)        # equal but distinct
False

The id is not portable across runs and not meaningful beyond “same object or different object.”

  • hash() – the hash value of an object, the same number dict and set use to look it up. Two equal objects hash to the same value; only hashable types (immutable values, mostly) work at all.

>>> hash("abc")           # some integer, build-dependent
-1600925533
>>> hash([1, 2])
TypeError: unhashable type: 'list'

5.33.2. Querying types and callability

  • type() – the exact class of a value. type(x) is int asks “is x exactly an int” (no subclasses); isinstance() is usually what you want instead.

  • isinstance() – “is x an instance of this class, or a subclass of it?” The standard tool for type-based dispatch inside functions.

  • issubclass() – the class-level counterpart. Takes two classes rather than an instance.

  • callable()True if the argument can be called with (). Useful when you receive an argument that may be a function or may be a plain value.

>>> isinstance(3, int)
True
>>> isinstance(True, int)        # bool is a subclass of int
True
>>> issubclass(bool, int)
True
>>> callable(len)
True
>>> callable(10)
False

A pattern that uses callable:

def call_or_return(x):
    return x() if callable(x) else x

5.33.3. Looking at scopes

  • globals() – the module’s global namespace as a dict. Reading from it works; writing to it is real, but doing so outside of REPL exploration makes a program difficult to follow.

  • locals() – the local namespace at the call site. Inside a function it reflects the local variables; modifying the returned dict is not guaranteed to write back into the actual locals (implementation-defined behaviour).

name = "OpenMV"

def f():
    x = 10
    print(globals()["name"])    # OpenMV
    print(locals())             # {'x': 10}

These two are useful for debugging and for tools that need to discover what is defined. Reach for them sparingly in regular code – a function that mutates globals() is one of the hardest things to reason about in Python.

5.33.4. See also

dir() and help(), covered in debugging, are the everyday introspection tools for exploring an unknown object’s surface.