元类和 __prepare__ ()
metaclass and __prepare__ ()
我正在自学 __prepare__
函数。我在 PEP3115
看到了这个片段
# 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
class 中获取属性?我看到 __prepare__
函数 returns 是 member_table 的实例,但是 member_table()
和 classdict.member_names
之间的 link 是如何生成的?
非常感谢大家!
这非常简单,因为这正是 prepare 所做的。
3.3.3.3. Preparing the class namespace Once the appropriate metaclass has been identified, then the class namespace is prepared. If the
metaclass has a __prepare__
attribute, it is called as namespace =
metaclass.__prepare__(name, bases, **kwds)
(where the additional
keyword arguments, if any, come from the class definition).
If the metaclass has no __prepare__
attribute, then the class
namespace is initialised as an empty ordered mapping.
https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace
这意味着,传递给 metaclass __new__
和 __init__
方法的 classdict
属性是完全相同的对象,即 return由 __prepare__
编辑。
那个对象应该是一个映射实例,也就是说,一个行为像字典的对象并且至少有 __setitem__
方法。此 __setitem__
方法由 Python 为声明的 class 主体本身内设置的所有变量调用。
也就是说,对于没有自定义元class的普通class,变量记录在字典中(有序字典,自Python 3.6 起)。
当 Python 运行 class 主体内的每个语句时,就会发生这种情况。如果在 class 正文中调用 locals()
,这就是 returned 的同一个对象:
In [21]: class M(type):
...: @classmethod
...: def __prepare__(cls, *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
最初设计此功能时的主要用例可能正是使 class 体内的声明顺序有意义的可能性。也就是说,__prepare__
方法可以只是 return 一个 collections.OrderedDict
实例,而 __new__
或 __init__
将按照该顺序执行。从 Python 3.6 开始,class 属性的排序是默认的 - 而 __prepare__
功能仍然非常先进,人们真的不得不考虑它的用途。
编辑: 通常 __prepare__
作为 class 方法实现,因为它在 meta 之前被调用class 已实例化。
我正在自学 __prepare__
函数。我在 PEP3115
# 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
class 中获取属性?我看到 __prepare__
函数 returns 是 member_table 的实例,但是 member_table()
和 classdict.member_names
之间的 link 是如何生成的?
非常感谢大家!
这非常简单,因为这正是 prepare 所做的。
3.3.3.3. Preparing the class namespace Once the appropriate metaclass has been identified, then the class namespace is prepared. If the metaclass has a
__prepare__
attribute, it is called asnamespace = metaclass.__prepare__(name, bases, **kwds)
(where the additional keyword arguments, if any, come from the class definition).If the metaclass has no
__prepare__
attribute, then the class namespace is initialised as an empty ordered mapping.
https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace
这意味着,传递给 metaclass __new__
和 __init__
方法的 classdict
属性是完全相同的对象,即 return由 __prepare__
编辑。
那个对象应该是一个映射实例,也就是说,一个行为像字典的对象并且至少有 __setitem__
方法。此 __setitem__
方法由 Python 为声明的 class 主体本身内设置的所有变量调用。
也就是说,对于没有自定义元class的普通class,变量记录在字典中(有序字典,自Python 3.6 起)。
当 Python 运行 class 主体内的每个语句时,就会发生这种情况。如果在 class 正文中调用 locals()
,这就是 returned 的同一个对象:
In [21]: class M(type):
...: @classmethod
...: def __prepare__(cls, *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
最初设计此功能时的主要用例可能正是使 class 体内的声明顺序有意义的可能性。也就是说,__prepare__
方法可以只是 return 一个 collections.OrderedDict
实例,而 __new__
或 __init__
将按照该顺序执行。从 Python 3.6 开始,class 属性的排序是默认的 - 而 __prepare__
功能仍然非常先进,人们真的不得不考虑它的用途。
编辑: 通常 __prepare__
作为 class 方法实现,因为它在 meta 之前被调用class 已实例化。