inspect.getmembers_static() lists attributes without using normal dynamic resolution. Unlike inspect.getmembers(), it avoids invoking many descriptors, properties, and custom __getattr__ or __getattribute__ paths. This makes it useful for documentation tools, debuggers, class analyzers, and API auditing.
The trade-off is that results may contain raw descriptor objects instead of computed values, and attributes created only at runtime may be absent.
Basic example
import inspect
class Example:
@property
def value(self):
print("property executed")
return 42
obj = Example()
print(inspect.getmembers_static(obj))
The property appears as a descriptor without executing its getter.
Difference from getmembers
inspect.getmembers(obj) performs normal attribute access and may execute arbitrary code. A property can open files, access a network, raise exceptions, or change state.
Filtering with predicate
members = inspect.getmembers_static(
Example,
predicate=inspect.isfunction,
)
The predicate receives each static value. Because descriptors are not bound, values can differ from normal attribute access.
Raw descriptors
Static inspection can return property, staticmethod, classmethod, and custom descriptor objects. This is useful when a tool needs the definition rather than the computed result.
for name, value in inspect.getmembers_static(Example):
if isinstance(value, property):
print(name, value.fget)
Inheritance and origin
To identify where an attribute was defined, walk the class MRO and inspect each class dictionary.
for cls in Example.__mro__:
if "value" in vars(cls):
print("defined on", cls)
Dynamic attributes may be missing
class Dynamic:
def __getattr__(self, name):
if name.startswith("field_"):
return name.upper()
raise AttributeError(name)
Those names are synthesized rather than stored, so static inspection does not discover them. That is expected.
Not a security sandbox
The function reduces accidental descriptor execution, but introspection is not isolation. Do not import untrusted modules or deserialize unsafe objects merely to analyze them.
Documentation generation
Documentation tools can collect descriptors, docstrings, and definitions statically, then explicitly resolve only trusted members.
Auditing properties
def list_properties(cls):
return {
name: value
for name, value in inspect.getmembers_static(cls)
if isinstance(value, property)
}
staticmethod and classmethod
Normal access binds methods. Static inspection may return the wrapper object. Use __func__ when the original function must be analyzed.
Metaclasses
Metaclasses can synthesize attributes. Static inspection separates stored definitions from runtime behavior. This complements the internal guide to DynamicClassAttribute.
Comparing results
normal = {name for name, _ in inspect.getmembers(obj)}
static = {name for name, _ in inspect.getmembers_static(obj)}
print(normal - static)
The difference may reveal dynamic members. Run normal lookup only when its side effects are acceptable.
Common mistakes
- Expecting computed property values.
- Treating a raw
staticmethodas a bound function. - Concluding that a dynamic attribute does not exist.
- Calling normal inspection on unknown objects first.
- Treating introspection as a security boundary.
Recommended practice
Start with static inspection, recognize descriptor types explicitly, walk the MRO when origin matters, and resolve dynamic values only for trusted members. See the internal guides to inspect and inspect.signature.
Conclusion
inspect.getmembers_static exposes object structure while avoiding much dynamic execution. It is especially valuable for documentation, auditing, and descriptor analysis.







