我正在从2.7迁移到3.x,并且我试图了解PEP3115中引入的元类的__prepare__
方法。
在我看过的大多数示例中,此方法的实现忽略了参数(name
,bases
和**kwargs
)只返回一个自定义词典对于元类的__new__
和__init__
方法提供的命名空间很有意思。即使example in PEP3115也没有对参数做任何事情。
我不怀疑__prepare__
签名有充分理由,但我还没有看到用例。
有哪些好的例子可以证明__prepare__
签名的合理性取这些参数?
答案 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 __
中做到这一点以下是类创建工作流程的图像,它更加清晰:
[抱歉,我找不到这张照片的原始来源]
答案 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
,目标类名称直接在第一个参数中传递给它。