我正在尝试使用PEP 3115中描述的“有序类”(即,可以按声明的顺序访问其成员的类).给出的实现是
# The custom dictionary
class member_table(dict):
def __init__(self):
self.member_names = []
def __setitem__(self, key, value):
# if the key is not already defined, add to the
# list of keys.
if key not in self:
self.member_names.append(key)
# Call superclass
dict.__setitem__(self, key, value)
# The metaclass
class OrderedClass(type):
# The prepare function
@classmethod
def __prepare__(metacls, name, bases): # No keywords in this case
return member_table()
# The metaclass invocation
def __new__(cls, name, bases, classdict):
# Note that we replace the classdict with a regular
# dict before passing it to the superclass, so that we
# don't continue to record member names after the class
# has been created.
result = type.__new__(cls, name, bases, dict(classdict))
result.member_names = classdict.member_names
return result
class MyClass(metaclass=OrderedClass):
# method1 goes in array element 0
def method1(self):
pass
# method2 goes in array element 1
def method2(self):
pass
有一些我很困惑的事情.首先,为什么__prepare__是一种类方法?该定义不使用metacls – 这只是一个约定吗?
其次,当我尝试这段代码时,’__ module__’在’method1’和’method2’之前的MyClass.member_names中结束,显然与声称’method1’是第一个元素的注释相矛盾.为什么这个特殊属性最终会出现在列表中而没有其他属性呢?有没有其他可能让我感到惊讶的事情(除了__doc__,如果类有文档字符串,我明确定义了什么)?
最后,此实现不从基类检索member_names.如果我想实现这一点,那么__prepare__的以下更改是否有任何问题(除了它不检查重复之外)?
@classmethod
def __prepare__(metacls, name, bases):
prep_dict = member_table()
for base in bases:
try:
prep_dict.member_names.extend(base.member_names)
except AttributeError:
pass
return prep_dict
解决方法:
Firstly, is there a reason why
__prepare__
is a classmethod? The definition doesn’t use metacls – is this just a convention?
正如评论中所指出的,当调用__prepare__时,类本身尚未实例化.这意味着,如果它是普通方法(使用self作为第一个参数) – 在此调用中没有值成为self.
这是有道理的,因为__prepare__的作用是在类体的解析中返回类似于对象的dict而不是普通的dict – 因此它根本不依赖于正在创建的类.
如果如评论中所提到的那样,它是一个静态方法(意味着它不会得到元数据的第一个参数),那么__prepare__将无法访问元类的任何其他方法 – 这是不必要的限制.
是的,就像self一样,在这种情况下,metacls只是一个约定 – 一个普通的Python标识符. (注意,当在普通类中使用classmethods时,第一个参数通常表示为cls而不是metacls.)
Secondly, when I try this code,
__module__
ends up in MyClass.member_names beforemethod1
andmethod2
, apparently contradicting the comments which claimmethod1
is the first element. Why does this special attribute end up in the list while no others do? Are there any others that might surprise me (apart from__doc__
if the class has a docstring, and any I define explicitly)?
可能是因为类的__module__特殊成员在考虑了元类的__prepare__方法后才想到了. (我无法通过Google搜索定义__module__的PEP来验证,但我敢打赌这是这种情况.)
不,不能保证未来的Python版本不会在字典中的显式类成员之前添加更多魔术属性.
但是对于元类,你完全可以控制 – 你可以像对象一样调整你的dict
(代码中的member_table)不计算以“__”开头的任何属性 – 例如.或者甚至根本不添加到最终的类实例(冒着以这种方式定义类的风险,而不使用某些Python特性).
Finally, this implementation does not retrieve the member_names from base classes. If I want to achieve that, is there anything wrong with the following change to
__prepare__
?
通过阅读它,我认为您提出的实施没有任何问题,但当然,您必须对其进行测试.
更新(2013-06-30):这个主题正在Python开发人员列表中进行讨论,它看起来像Python 3.4所有类都将默认排序,不需要使用元类或__prepare__来进行排序