【发布时间】:2012-01-05 03:58:19
【问题描述】:
我正在尝试使用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__ 是classmethod 有什么原因吗?该定义不使用metacls - 这只是一个约定吗?
其次,当我尝试这段代码时,'__module__' 在'method1' 和'method2' 之前出现在MyClass.member_names,这显然与声称'method1' 是第一个元素的cmets 相矛盾。为什么这个特殊属性最终出现在列表中,而其他属性没有?还有其他可能让我感到惊讶的吗(除了 __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
【问题讨论】:
-
__prepare__将使用 2 个参数,但当以MyClass.__prepare__调用时,它将被绑定为 MyClass 的方法。无论您是否会这样做,最好将其绑定到元类并使用第一个参数获取mcls实例。我想你可以改成staticmethod。 -
PEP 说“在评估类主体之前,将名为
__prepare__的属性作为函数调用”。所以它必须是类方法或静态方法,而不是实例方法,因为此时尚未调用(实例化)元类。 -
感谢 eryksun 和 yak,但我不确定我是否完全理解 - 我想我对
classmethod的确切作用有点天真。我知道如果__prepare__未声明为classmethod,它会接收name和bases作为参数,而如果它是classmethod,它也会接收类-但classmethod有什么其他效果在这种情况下?另外,从元类继承的方法没有出现在dir(MyClass)中是否有原因? -
正如 yak 所解释的,
__prepare__被查找为属性,而不是方法。如果省略 classmethod 装饰器,则还需要省略 metacls 参数。除了添加 metacls 参数之外,classmethod 装饰器没有其他作用。请注意,OrderedClass是MyClass的元类,而不是基类,因此 MyClass 不继承自 OrderedClass。__module__属性可能很特殊,因为它必须在创建类时添加,并且不能从对象继承。您可以在member_table.__setitem__方法中将其过滤掉。
标签: python python-3.x metaclass prepare