为什么__prepare__将name和bases作为参数

时间:2017-12-21 03:40:59

标签: python python-3.x metaclass

我正在从2.7迁移到3.x,并且我试图了解PEP3115中引入的元类的__prepare__方法。

在我看过的大多数示例中,此方法的实现忽略了参数(namebases**kwargs)只返回一个自定义词典对于元类的__new____init__方法提供的命名空间很有意思。即使example in PEP3115也没有对参数做任何事情。

我不怀疑__prepare__签名有充分理由,但我还没有看到用例。

有哪些好的例子可以证明__prepare__签名的合理性取这些参数?

2 个答案:

答案 0 :(得分:0)

__ prepare__会像你说的那样为类创建一个命名空间,所以我们可以在里面做一些逻辑:

class MyMeta(type):
    @classmethod
    def __prepare__(metacls, klass_name, bases):
        namespace = {'s': 'my string',
                     'description': 'N/A'}
        if klass_name.endswith('Base'):
            namespace.update({'description': 'base class'})

        return namespace

class KlassBase(metaclass=MyMeta):
    def __init__(self, value):
        self.value = value

class SubKlass(KlassBase):
    def __init__(self, value):
        super().__init__(value)

print(KlassBase(5).s)
print(KlassBase(5).description)
print(SubKlass(5).s)
print(SubKlass(5).description)

你得到了:

my string
base class
my string
N/A

我们不这样做的原因,因为同样的事情可以在元类的其他部分完成,如:__ new __,__ init__,或被后者覆盖。所以大多数时候,我们都不会在__prepare __

中做到这一点

以下是类创建工作流程的图像,它更加清晰:

enter image description here

[抱歉,我找不到这张照片的原始来源]

答案 1 :(得分:0)

当你查看__prepare__以及当时Python类创建机制的状态时,很明显真正需要的是一种启用属性顺序保存的机制。

通过这种方式,人们可以创建用于描述具有有序字段的数据记录的类,这几乎是人们在描述记录时所期望的。 (试着想象一下每次渲染的形式,它会改变场地的数量,所以有一半的时间你会填满你居住在这个国家之前的国家)。

而不是一个固定的机制,只是让类体名称空间成为collections.OrderedDict,它们提出了__prepare__,它可以轻松实现这一点,只需一行返回一个新的OrderedDict实例。

但是__prepare__可以有如此多的使用和滥用,我认为没有人真正想到所有的可能性。你提到的参数在被调用时是可用的,并且因为它存在,所以没有任何理由不将它们传递给函数。为什么要削弱对课堂的了解? __prepare__函数中的名字?

因此,它只是一个强大而灵活的机制,并不一定在制作时考虑所有可能的用例。 "有序属性"事情另一方面非常重要,即使没有任何自定义元类声明,它也成为Python 3.6的默认设置,即PEP 520。

例如,

__prepare__ receivog base允许用一些在超类名称空间中找到的对象预填充名称空间,例如,覆盖继承属性访问。 或者它可以简单地针对预先存在的注册表检查类名,并从那里引发或预先填充内容。

一个超级简单的用法是预先填充__name__,以便类属性可以使用它:

import collections

class M(type):
    @classmethod
    def __prepare__(metacls, name, bases):
        ns = collections.OrderedDict()
        ns["__name__"] = name

class T(metaclass=M):
    __table__ = f"_{__name__.lower()}"

琐事

由于函数作为Python 3中的方法起作用的方式,一个有趣的相关内容没有在任何地方记录,如果__prepare__没有明确地装饰成classmethod,它的工作原理作为staticmethod ,目标类名称直接在第一个参数中传递给它。