Dynamic Members and Static Structure¶
Page Maps¶
graph LR
family["Python Programming"]
program["Python Meta-Programming"]
section["Signatures Provenance Runtime Evidence"]
page["Dynamic Members and Static Structure"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
orient["Orient on the page map"] --> read["Read the main claim and examples"]
read --> inspect["Inspect the related code, proof, or capstone surface"]
inspect --> verify["Run or review the verification path"]
verify --> apply["Apply the idea back to the module and capstone"]
By Module 03, the course has enough pieces to name one of the most important inspection choices in metaprogramming:
do you want the value normal lookup would produce, or do you want the raw structure attached to the object?
This page puts that choice into concrete tooling terms with:
inspect.getmembersinspect.getattr_static
The sentence to keep¶
When enumerating members, ask:
am I trying to collect dynamic values, or am I trying to inspect attached structure without executing runtime behavior?
That question should drive the tool choice.
inspect.getmembers is dynamic enumeration¶
inspect.getmembers(obj, predicate=None) works roughly like this:
- discover names with
dir(obj) - call
getattr(obj, name)for each name - optionally filter the resulting values with a predicate
- return sorted
(name, value)pairs
That is useful, but it is not passive.
Because it uses dynamic lookup, it can:
- execute properties
- trigger descriptors
- trigger
__getattr__ - activate proxy behavior
So getmembers is a value-oriented tool, not a structural-inspection tool.
inspect.getattr_static is structural inspection¶
inspect.getattr_static(obj, name) tries to retrieve the raw attribute object without
running normal lookup behavior.
That means it can return:
- a property object
- a function stored on the class
- a descriptor object
- a plain attached value
without automatically executing it.
That is why it fits library and framework introspection much better when the goal is structure rather than behavior.
One picture of the difference¶
graph TD
dynamic["Dynamic enumeration<br/>getmembers(obj)"]
dir["dir(obj)"]
getattr["getattr(obj, name)"]
effects["descriptor execution<br/>property evaluation<br/>fallback hooks"]
static["Static lookup<br/>getattr_static(obj, name)"]
raw["raw attached object"]
dynamic --> dir --> getattr --> effects
static --> raw
Caption: one path collects resolved values, the other inspects what is attached before runtime behavior runs.
A property shows the difference clearly¶
import inspect
class Example:
@property
def expensive(self):
print("SIDE EFFECT: property executed")
return 123
def method(self):
pass
obj = Example()
_ = inspect.getmembers(obj)
raw = inspect.getattr_static(Example, "expensive")
assert isinstance(raw, property)
The printed side effect is the important boundary:
getmembersresolved the value dynamicallygetattr_staticrevealed the attached structure without evaluation
Structural enumeration often needs a custom helper¶
Because getmembers is dynamic, framework or tooling code often wants its own structural
enumeration helper:
import inspect
def static_getmembers(obj, predicate=None):
for name in dir(obj):
value = inspect.getattr_static(obj, name)
if predicate is None or predicate(value):
yield name, value
That helper still inherits the lower-risk caveat around dir(obj), but it avoids the
larger mistake of resolving every name dynamically by default.
Use the tool that matches the question¶
Good reasons to use getmembers:
- quick REPL exploration
- controlled debugging where executing descriptors is acceptable
- value-oriented inspection when you explicitly want runtime semantics
Good reasons to use getattr_static:
- framework discovery
- manifest or schema extraction
- class and descriptor inspection
- any tool that should not trigger business behavior while observing structure
The key discipline is not to use the dynamic tool by habit when the question is clearly structural.
Static structure is often the stronger review surface¶
When reviewing framework code, the structural question is often the one that matters most:
- what descriptors are attached?
- what functions are defined on the class?
- what property objects exist?
- what raw members make up the framework contract?
Dynamic values may be interesting later, but they are not the first truth the reviewer needs.
Review rules for member inspection¶
When reviewing member-inspection code, keep these questions close:
- is the code trying to inspect attached structure or resolved runtime values?
- is
getmembersbeing used wheregetattr_staticwould better match the tool's purpose? - does the enumeration path risk executing properties or proxy behavior unintentionally?
- is a custom structural helper justified instead of broad dynamic enumeration?
- does the code document when it chooses to cross from structure into value resolution?
What to practice from this page¶
Try these before moving on:
- Compare
getmembers(obj)with astatic_getmembers(obj)helper on a class that has a property. - Use
getattr_staticto list properties on a class without evaluating them. - Explain one case where dynamic member values are the right goal and one where raw structure is the right goal.
If those feel ordinary, the final core can close the module with the highest-risk introspection surface here: frames and stack inspection.
Continue through Module 03¶
- Previous: Provenance Helpers and Best-Effort Recovery
- Next: Frames and Diagnostic-Only Runtime Evidence
- Practice: Exercises
- Terms: Glossary