AI Skill Report Card
Introspecting Python Objects
Quick Start14 / 15
Pythonclass Animal: """Base class for animals.""" pass class Dog(Animal): """A dog.""" pass d = Dog() d.__class__ # <class '__main__.Dog'> d.__dict__ # {} (instance attribute dict) Dog.__name__ # 'Dog' Dog.__qualname__ # 'Dog' Dog.__mro__ # (Dog, Animal, object) Dog.__bases__ # (Animal,) Dog.__doc__ # 'A dog.' Dog.__module__ # '__main__'
Recommendation▾
Add an example showing practical use in a debugging/serialization tool (e.g., generic repr builder using __dict__ and __class__) to tie introspection to real-world tasks mentioned in the description.
Workflow13 / 15
Progress:
- Identify what kind of introspection is needed (identity, hierarchy, namespace, or metadata)
- Pick the correct special attribute(s) for that object type
- Handle attributes that may not exist (use
getattrwith defaults orhasattr) - Distinguish instance-level vs class-level attribute access
- Validate results against edge cases (built-ins, slots, dynamic classes)
Step 1: Identify the introspection goal
| Goal | Attribute(s) |
|---|---|
| Get the class of an instance | obj.__class__ |
| Get a class/function/module's bare name | __name__ |
| Get the dotted path to where it was defined | __qualname__ |
| Get inheritance chain (method resolution order) | __mro__ (classes only, not instances) |
| Get direct parent classes | __bases__ |
| Get instance/class namespace dict | __dict__ |
| Get docstring | __doc__ |
| Get defining module's name | __module__ |
| Get allowed instance attribute names (memory-optimized classes) | __slots__ |
Step 2: Choose instance vs. class access
type(obj).__mro__works;obj.__mro__does not (MRO is a class-only attribute).obj.__dict__gives per-instance attributes;type(obj).__dict__gives the class namespace (amappingproxy, read-only).- Objects using
__slots__have no__dict__unless__dict__is explicitly included in slots.
Step 3: Guard against missing attributes
Not all objects define every special attribute (e.g., built-in types, __slots__ classes lack __dict__; lambdas have __qualname__ like <locals>.<lambda>). Always prefer getattr(obj, '__dict__', None) or hasattr in generic tooling.
Recommendation▾
Include a brief note on __getattr__/__getattribute__ or dir() as complementary introspection tools, since the skill scope mentions reflection code broadly.
Examples17 / 20
Example 1: Inspecting instance vs class dict Input:
Pythonclass Point: def __init__(self, x, y): self.x = x self.y = y p = Point(1, 2)
Output:
Pythonp.__dict__ # {'x': 1, 'y': 2} Point.__dict__ # mappingproxy({'__init__': <function...>, '__dict__': <attribute...>, '__weakref__': <attribute...>, '__doc__': None})
Example 2: Walking the MRO for a diamond hierarchy Input:
Pythonclass A: pass class B(A): pass class C(A): pass class D(B, C): pass D.__mro__
Output:
Python(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
Example 3: Qualname vs name for nested definitions Input:
Pythonclass Outer: class Inner: def method(self): pass Outer.Inner.method.__name__ Outer.Inner.method.__qualname__
Output:
Python'method' 'Outer.Inner.method'
Example 4: Slots preventing dict Input:
Pythonclass Slotted: __slots__ = ('x',) s = Slotted() s.x = 5 s.__dict__
Output:
PythonAttributeError: 'Slotted' object has no attribute '__dict__'
Recommendation▾
The workflow checklist is somewhat generic; tighten it to map more directly to the specific attribute-selection table in Step 1 for faster actionability.
Best Practices
- Use
type(obj)rather thanobj.__class__when correctness matters, since__class__can be overridden (e.g., proxy objects) — but__class__is fine for typical introspection/debugging. - Use
__qualname__over__name__in logs/tracebacks when nested classes or closures are involved — it disambiguates identically named functions/classes. - For serialization or debugging tools, iterate
vars(obj)(equivalent toobj.__dict__) rather than accessing__dict__directly —vars()raises a clearerTypeErrorwhen unsupported. - When building generic MRO/hierarchy tools, call
.__mro__on the type, not the instance:type(obj).__mro__. - Check
__module__alongside__qualname__to fully disambiguate identically named classes from different modules.
Common Pitfalls
- Calling
obj.__mro__directly on an instance — it's only defined on classes (viatype). Usetype(obj).__mro__. - Assuming every object has
__dict__— built-ins (int,str) and__slots__-based classes often don't. - Mutating
type(obj).__dict__directly — it's a read-onlymappingproxy; usesetattr(cls, name, value)instead. - Confusing
__name__with__qualname__for nested or locally-defined functions/classes, leading to ambiguous logs. - Relying on
__module__for dynamically created classes (e.g., viatype()in a factory) — it may default to the factory's module, not the intended logical one.