Credit: Jürgen Hermann, Alex Martelli
You need to map all members of a class, including inherited members, into a dictionary of class attribute names.
Here is a solution that works portably and transparently on both new-style (Python 2.2) and classic classes with any Python version:
def all_members(aClass): try: # Try getting all relevant classes in method-resolution order mro = list(aClass._ _mro_ _) except AttributeError: # If a class has no _ _mro_ _, then it's a classic class def getmro(aClass, recurse): mro = [aClass] for base in aClass._ _bases_ _: mro.extend(recurse(base, recurse)) return mro mro = getmro(aClass, getmro) mro.reverse( ) members = {} for someClass in mro: members.update(vars(someClass)) return members
The
all_members
function in this recipe creates a dictionary that includes each
member (such as methods and data attributes) of a class with the name
as the key and the class attribute value as the corresponding value.
Here’s a usage example:
class Eggs: eggs = 'eggs' spam = None class Spam: spam = 'spam' class Breakfast(Spam, Eggs): eggs = 'scrambled' print all_members(Eggs) print all_members(Spam) print all_members(Breakfast)
And here’s the output of this example (note that the order in which each dictionary’s items are printed is arbitrary and may vary between Python interpreters):
{'spam': None, '_ _doc_ _': None, 'eggs': 'eggs', '_ _module_ _': '_ _main_ _'} {'spam': 'spam', '_ _doc_ _': None, '_ _module_ _': '_ _main_ _'} {'_ _doc_ _': None, 'eggs': 'scrambled', 'spam': 'spam', '_ _module_ _': '_ _main_ _'}
After constructing the dictionary d
with
d=all_members(c)
, you can use d
for repeated introspection about class c
.
d.has_key(x)
is the same as
hasattr(c,x)
, and d.get(x)
is
the same as getattr(c,x,None)
, but it
doesn’t repeat the dynamic search procedure each
time. Apart from the order of its items, d.keys
is
like dir(c)
if c
is a new-style
class (for which dir
also returns the names of
inherited attributes) but is richer and potentially more useful than
dir(c)
if c
is a classic class
(for which dir
does not list inherited attributes,
only attributes defined or overridden directly in class
c
itself).
The
all_members
function starts by getting a list of all relevant classes (the class
itself and all of its bases, direct and indirect), in the order in
which attributes are looked up, in the
mro
variable (MRO stands for
method-resolution order). This happens immediately for a new-style
class, since it exposes this information with its _ _mro_ _
attribute—we just need to build a list from it,
since it is a tuple. If accessing _ _mro_ _
fails,
we’re dealing with a classic class and must build
mro
up in a recursive way. We do that in the
nested function getmro
in the
except
clause. Note that we give
getmro
itself as an argument to facilitate
recursion in older Python versions that did not support lexically
nested scopes.
Once we have mro
, we need to
reverse
it, because we build up our dictionary
with the update
method. When we call
adict.update(anotherdict)
, the entries in the two
dictionaries adict
and
anotherdict
are merged as the new contents of
adict
. In case of conflict (i.e., a key
k
is present in both dictionaries), the value used
is anotherdict[k]
, which overrides the previous
value of adict[k]
. Therefore, we must build our
dictionary starting with the classes that are looked up last when
Python is looking for an attribute. We move towards the classes that
are looked up earlier to reproduce how overriding works with
inheritance. The dictionaries we merge in this way are those given
sequentially by the built-in function vars
on each
class. vars
takes any object as its argument and
returns a dictionary of the object’s attributes.
Note that even for new-style classes in Python 2.2,
vars
does not consider inherited attributes, just
the attributes defined or overridden directly in the object itself,
as dir
does only for classic classes.
Understanding method resolution order is a new challenge even for old Python hands. The best description is in Guido’s essay describing the unification of types and classes (http://www.python.org/2.2/descrintro.html#mro), which was refined somewhat in PEP 253 (http://www.python.org/peps/pep-0253.html).
Get Python Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.