元类和__prepare__()
我在教我自己关于__prepare__
函数。我看到这个片段在PEP3115元类和__prepare__()
# 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
我的问题是在这条线: result.member_names = classdict.member_names
怎么能变classdict
得到的member_table
类的属性?我看到__prepare__
函数返回member_table的一个实例,但是如何生成member_table()
和classdict.member_names
之间的链接?
非常感谢大家!
这是非常简单的,因为它准备做什么。
3.3.3.3。准备类名称空间一旦确定了适当的元类,就准备好了类名称空间。如果 元类具有
__prepare__
属性,则将其称为namespace = metaclass.__prepare__(name, bases, **kwds)
(其中附加的 关键字参数(如果有的话)来自类定义)。如果元类没有
__prepare__
属性,那么类 命名空间被初始化为一个空的有序映射。
https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace
这意味着,被传递到元类__new__
和__init__
方法classdict
属性正是由__prepare__
返回相同的对象。
该对象应该是一个映射实例,也就是说,一个对象的行为类似于字典,并且至少具有__setitem__
方法。这个__setitem__
方法被Python调用,用于在声明的类体内部设置的所有变量。
也就是说,对于没有自定义元类的普通类,变量被记录在一个字典(一个有序的字典,如Python 3.6)中。
这发生在Python运行类体内的每个语句时。这是返回一个应该叫locals()
类体内相同的对象:
In [21]: class M(type):
...: def __prepare__(self, *args):
...: class CustomDict(dict):
...: __repr__ = lambda self: "I am a custom dict: " + str(id(self))
...: namespace = CustomDict()
...: print("From __prepare__", namespace)
...: return namespace
...:
...: def __new__(metacls, name, bases, namespace):
...: print("From __new__:", namespace)
...: return super().__new__(metacls, name, bases, namespace)
...:
...:
In [22]: class Test(metaclass=M):
...: def __init__(self):
...: ...
...: print("From class body:", locals(), locals()["__init__"])
...:
...:
From __prepare__ I am a custom dict: 140560887720440
From class body: I am a custom dict: 140560887720440 <function Test.__init__ at 0x7fd6e1bd7158>
From __new__: I am a custom dict: 140560887720440
主要用例时,首先设计该功能可能正是使一类的身体有意义内声明的顺序的可能性。也就是说,__prepare__
方法可能会返回一个collections.OrderedDict
实例,并且__new__
或__init__
将按该顺序执行操作。从Python 3.6开始,类属性的排序是默认的 - 而且__prepare__
功能仍然非常先进,因此必须考虑它的用途。
感谢您的回答!还有一个小问题 - 我们能否确保'__prepare__'函数总是返回是字典或装饰字典类型(不是其他类型)的名称空间? – luoshao23
“确定”是什么意思?你应该编写你的'__prepare__'并且知道它返回的结果。如果它不必像一个完整的映射对象那样工作 - 它只需要'__setitem__',并且mclass'__init__'中的代码知道如何处理它。 – jsbueno
(如果您的问题已得到解答,请不要忘记接受答案) – jsbueno