文章来自《Python cookbook》.

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

-- 60.7.17.36 [2004-11-01 07:39:36]

1. 描述

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

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

Credit: Jürgen Hermann, Alex Martelli

1.1. 问题 Problem

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

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

1.2. 解决 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

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

在使用d=all_memebers(c)构建字典d之后, 可以多次使用introspectoin来查询类c。判断类c 是否含有属性x使用: d.has_key(x), 效果同 hasattr(c,x)是一样的,获的类c的x属性使用 d.get(x),同getattr(c,x,None)效果也相同。使用字典d.has_key(x),以及d.get(x), 效率上得到提升, 不需要每次动态查询类的继承层次. 对于新的类c, 除属性的输出顺序外, 使用d.keys和dir(c)效果一样,但是对于旧的类(类的dir仅列出本身或覆盖的属性,并不列出继承的属性),has_memebers函数可能更有用些。

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.

all_members函数开始获得类层次中包含的相关类(类本身和直接和间接基类),按照属性查找的顺序,添加到变量mro(表示 method-resolution order 方法调用的解析顺序)中。对于一个新的提供了元组属性_ _ mro_ _的Python类,处理很快,只要将元组转换为list就可以了.如果访问_ _micro_ _失败,那么我们处理的是一个古典类,必须递归处理类层次,构造mro变量.代码中在except部分进行了递归处理.注意我们将函数getmtro作为递归参数传入,这样可以处理python的旧版本中不支持嵌套语法作用域。(译注: 这句不懂!).

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.

获得list变量mro后,由于使用它构建字典, 需要反转这个list,原应如下:当调用adict.update(anotherdict)时,两个字典中都存在的键对应的值会合并( #译注: a.update(b) for k in b.keys(): a[k] = b[k]). 因此, 必须从属性查找解析的最后处开始构建属性字典。

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.

然后沿着属性解析的类顺序的逆序处理各种属性,同时很好的解决的属性覆盖的问题。更新时使用的字典参数是调用内置函数 vars处理每个类得到的。vars参数是任意对象,返回参数属性的字典。 注意即时对于2.2种新的类,vars也不会返回继承的属性,和dir处理经典类一样,仅仅返回本身定义的属性或者覆盖的属性。

1.4. 参考 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).

理解Python方法调用解析顺序是一个新的挑战,即使对Python Old Hands也是一样。对此的最佳解释见Guido的文章 (http://www.python.org/2.2/descrintro.html#mro)

以及PEP 253中进一步的阐述 (http://www.python.org/peps/pep-0253.html)