Navigating Python Stdtypes
When a question touches Python's built-in type behavior, identify which category it falls into first, then apply the specific semantics for that category:
Python# Category check example type([]) # <class 'list'> -> sequence type (mutable) type(()) # <class 'tuple'> -> sequence type (immutable) type({}) # <class 'dict'> -> mapping type type(set()) # <class 'set'> -> set type (mutable) type(frozenset()) # <class 'frozenset'> -> set type (immutable) type(1).__mro__ # int -> object -> numeric type type(lambda:0) # <class 'function'> -> "other built-in type" type(int) # <class 'type'> -> "other built-in type" (class) type(list[int]) # <class 'types.GenericAlias'> -> generic alias type
Progress:
- Identify the object's category (numeric, sequence, text, binary, set, mapping, or "other")
- For "other built-in types," determine the specific subtype (module, class, function, method, code, type, generic alias, ellipsis, notimplemented)
- Recall the defining attributes/protocol for that type (dunder methods, mutability, hashability)
- Apply or explain the relevant behavior with a minimal code example
- Note version-specific caveats (e.g., features added/changed in 3.9+, 3.12+)
Other Built-in Types Reference
These don't fit neatly into numeric/sequence/mapping/set but appear constantly in real code:
- Modules:
type(module) -> types.ModuleType. Attribute access via__dict__;importcreates these. - Classes and instances: Classes are created by
typeor a metaclass; instances via__call__. Key dunders:__init__,__new__,__class__. - Functions:
defandlambdaproducetypes.FunctionType. Have__code__,__defaults__,__closure__,__globals__. - Methods: Bound methods (
types.MethodType) wrap a function + instance (__self__,__func__). - Code objects:
func.__code__, produced by compilation; immutable, introspectable (co_varnames,co_consts). - Type objects:
typeitself;isinstance(x, type)checks ifxis a class. - The
NotImplementedsingleton: Returned by rich comparison / arithmetic dunders when an operation isn't supported for given operand types — never test truthiness ofNotImplemented, alwaysreturn NotImplementedand let Python retry with reflected method or raiseTypeError. - The
Ellipsissingleton (...): Used as a placeholder (type stubs, NumPy slicing, unimplemented function bodies). types.GenericAlias: Produced by subscripting builtin containers (list[int],dict[str, int]) — supports__origin__and__args__, used heavily for typing since PEP 585.
Example 1:
Input: "Why does list.__eq__(other) sometimes return NotImplemented instead of False?"
Output:
Pythonclass Point: def __eq__(self, other): if not isinstance(other, Point): return NotImplemented # let Python try other.__eq__(self) return self.x == other.x and self.y == other.y
NotImplemented signals "I don't know how to compare with this type" — distinct from False, which asserts inequality. Returning False outright breaks symmetric comparison with subclasses/other types; Python falls back to other.__eq__(self) or identity-based default only when NotImplemented is returned.
Example 2:
Input: "What's the type of dict[str, int] and why does it matter?"
Output: type(dict[str, int]) is types.GenericAlias. It matters because dict[str, int] is not itself a class you can subclass reliably pre-3.9 semantics without __class_getitem__, but it does support isinstance-adjacent introspection via __origin__ (dict) and __args__ ((str, int)) — used by type checkers and typing.get_origin()/get_args().
Example 3: Input: "How do I introspect a function's default arguments and closure variables?" Output:
Pythondef make_adder(n): def adder(x, y=1): return x + y + n return adder f = make_adder(10) f.__defaults__ # (1,) f.__closure__ # (<cell: contents=10>,) f.__code__.co_varnames # ('x', 'y')
- Distinguish mutable vs immutable built-ins early — it drives hashability (
tuplehashable if elements are;listnever) and safe use as dict keys/set members. - When comparing against
NotImplemented, useis, never==or truthiness (if result is NotImplemented). - Prefer
types.GenericAliasintrospection (get_origin,get_argsfromtyping) over manually parsing__args__/__origin__for forward compatibility. - For module/class/function introspection, prefer the
inspectmodule over raw dunder access when writing production tooling — it handles edge cases (bound vs unbound, wrapped functions viafunctools.wraps). - Check the Python version when relying on newer built-in type features (e.g., PEP 585 generic alias subscripting requires 3.9+; some
typesmodule additions are version-gated).
- Treating
NotImplementedas a boolean sentinel (if not result:) — it's truthy and this is a common source of silent bugs. - Assuming
type(x) == intinstead ofisinstance(x, int)— breaks withbool(subclass ofint) and other subclassing scenarios. - Confusing
Ellipsis(..., a real singleton object) with "not yet implemented" — it's valid runtime data, not just a stub marker. - Forgetting that bound methods (
instance.method) create a newMethodTypeobject on each attribute access —instance.method is instance.methodisFalse. - Mutating
__defaults__or assuming default arguments are re-evaluated per call — they're evaluated once at function definition time (the classic mutable-default-argument trap).