python - 元类和 __prepare__ ()

标签 python metaclass

我正在自学 __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 类中获取属性?我看到 __prepare__ 函数返回一个 member_table 实例,但是 member_table()classdict.member_names 之间的链接是如何生成的?

非常感谢大家!

最佳答案

这非常简单,因为这正是 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

也就是说,classdict传递到元类中的属性 __new____init__ methods 与 __prepare__ 返回的对象完全相同.

那个对象应该是一个映射实例,也就是说,一个行为像字典的对象并且至少有 __setitem__方法。这__setitem__方法由 Python 为声明的类主体本身内设置的所有变量调用。

也就是说,对于没有自定义元类的普通类,变量记录在字典中(有序字典,自 Python 3.6 起)。

当 Python 在类主体中运行每个语句时,就会发生这种情况。这是一次调用时返回的同一个对象 locals()在类(class)体内:

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

最初设计此功能时的主要用例可能正是使类体内的声明顺序有意义的可能性。即 __prepare__方法可以只返回 collections.OrderedDict实例,和 __new____init__将按照该命令行事。从 Python 3.6 开始,类属性的顺序默认为 - 和 __prepare__功能仍然如此先进,人们真的不得不想出它的用途。

编辑: 通常 __prepare__作为类方法实现,因为它在实例化元类之前被调用。

关于python - 元类和 __prepare__ (),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46827708/

相关文章:

python请求获取网页失败

python - 在不同的 ARM 架构上进行编程

python - python 元类中的 __call__() 方法

python - 理解 Python 中的元类和继承

python-3.x - 在 python 钩子(Hook) __prepare__ 中使用 **kwargs

python - 连接和分组填充 NaN 值

Python方法对象创建

Python:如何通过元类创建实例属性?

python - Cython 元类.pxd : How should I implement `__eq__()` ?

python - 在 Python 的 google spreadsheet api v4 中获取工作表列表和最新工作表