文章来自《Python cookbook》.

翻译仅仅是为了个人学习,其它商业版权纠纷与此无关!

-- 60.7.17.36 [DateTime(2004-11-01T07:39:36Z)] TableOfContents

描述

Getting All Members of a Class Hierarchy Credit: Jürgen Hermann, Alex Martelli

获得类层次中所有成员属性

Credit: Jürgen Hermann, Alex Martelli

问题 Problem

You need to map all members of a class, including inherited members, into a dictionary of class attribute names.

需要映射一个类的所有属性,包括继承的属性,到一个以类属性名称作为键的字典中。

解决 Solution

Here is a solution that works portably and transparently on both new-style (Python 2.2) and classic classes with any Python version:

一个可以在Python的各种新(2.2)旧版本中使用的解决方法如下:

   1 def all_members(aClass):
   2     try:
   3         # Try getting all relevant classes in method-resolution order
   4         mro = list(aClass._ _mro_ _)
   5     except AttributeError:
   6         # If a class has no _ _mro_ _, then it's a classic class
   7         def getmro(aClass, recurse):
   8             mro = [aClass]
   9             for base in aClass._ _bases_ _: mro.extend(recurse(base, recurse))
  10             return mro
  11         mro = getmro(aClass, getmro)
  12     mro.reverse(  )
  13     members = {}
  14     for someClass in mro: members.update(vars(someClass))
  15     return members

讨论 Discussion

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:

食谱中all_memebers函数创建了一个以类属性名称为键,属性值为对应字典值的字典。字典中包括了类的各种成员:函数成员和数据成员。下面是一个例子:

   1 class Eggs:
   2     eggs = 'eggs'
   3     spam = None
   4 
   5 class Spam:
   6     spam = 'spam'
   7 
   8 class Breakfast(Spam, Eggs):
   9     eggs = 'scrambled'
  10 
  11 print all_members(Eggs)
  12 print all_members(Spam)
  13 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):

输出如下(注意字典的输出顺序是任意的,并且可能由于Python解释器的不同而不同):

   1 {'spam': None, '_ _doc_ _': None, 'eggs': 'eggs', '_ _module_ _': '_ _main_ _'}
   2 {'spam': 'spam', '_ _doc_ _': None, '_ _module_ _': '_ _main_ _'}
   3 {'_ _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梬e 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.

参考 See Also

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).