【发布时间】:2011-09-27 14:22:32
【问题描述】:
在与元类发生冲突后,我深入研究了 Python 中的元编程主题,我有几个问题,恕我直言,在可用的文档中没有明确回答。
- 在元类中同时使用
__new__和__init__时,它们的参数必须定义相同吗? - 在元类中定义类
__init__的最有效方法是什么? - 有没有办法在元类中引用类实例(通常是self)?
【问题讨论】:
标签: python metaprogramming metaclass
在与元类发生冲突后,我深入研究了 Python 中的元编程主题,我有几个问题,恕我直言,在可用的文档中没有明确回答。
__new__ 和__init__ 时,它们的参数必须定义相同吗?__init__ 的最有效方法是什么?【问题讨论】:
标签: python metaprogramming metaclass
同时使用__new__ 和__init__ 时
在元类中,他们的论点必须
定义一样吗?
我认为Alex Martelli 解释了 最简洁的:
class Name(Base1,Base2): <<body>>
__metaclass__==suitable_metaclass
意思
Name = suitable_metaclass('Name', (Base1,Base2), <<dict-built-by-body>>)
所以别再想了 合适的元类作为元类 一会儿,只是把它当作一个 班级。每当你看到
suitable_metaclass('Name', (Base1,Base2), <<dict-built-by-body>>)
它告诉你
合适的元类的__new__
方法必须有签名
像
def __new__(metacls, name, bases, dct)
还有一个__init__ 之类的方法
def __init__(cls, name, bases, dct)
所以签名并不完全相同,但它们仅在第一个参数上有所不同。
什么是最有效的定义方式
元类中的__init__ 类?
高效是什么意思?它是
不需要定义__init__
除非你愿意。
有没有办法引用类 实例(通常是自我)在 元类?
不,您不需要这样做。 任何取决于班级的事情 实例应在处理 类定义,而不是在 元类。
【讨论】:
def __init__(cls, name, bases, dct) 非常标准。没有那么多选择,除了变量名的细微变化,例如klass 对应cls,或clsdict 对应dct 等。
对于 1:any 类的 __init__ 和 __new__ 必须接受相同的参数,因为它们将使用相同的参数调用。 __new__ 接受更多它忽略的参数是很常见的(例如,object.__new__ 接受任何参数并忽略它们)因此__new__ 在继承期间不必被覆盖,但通常只有在你有根本没有__new__。
这不是问题,因为如前所述,元类总是使用相同的参数集调用,因此您不会遇到麻烦。至少有论据。但是,如果您要修改传递给父类的参数,则需要同时修改它们。
对于 2:您通常不在元类中定义类 __init__。您可以编写一个包装器并在元类的__new__ 或__init__ 中替换类的__init__,或者您可以在元类上重新定义__call__。如果你使用继承,前者会表现得很奇怪。
import functools
class A(type):
def __call__(cls, *args, **kwargs):
r = super(A, cls).__call__(*args, **kwargs)
print "%s was instantiated" % (cls.__name__, )
print "the new instance is %r" % (r, )
return r
class B(type):
def __init__(cls, name, bases, dct):
super(B, cls).__init__(name, bases, dct)
if '__init__' not in dct:
return
old_init = dct['__init__']
@functools.wraps(old_init)
def __init__(self, *args, **kwargs):
old_init(self, *args, **kwargs)
print "%s (%s) was instantiated" % (type(self).__name__, cls.__name__)
print "the new instance is %r" % (self, )
cls.__init__ = __init__
class T1:
__metaclass__ = A
class T2:
__metaclass__ = B
def __init__(self):
pass
class T3(T2):
def __init__(self):
super(T3, self).__init__()
以及调用它的结果:
>>> T1()
T1 was instantiated
the new instance is <__main__.T1 object at 0x7f502c104290>
<__main__.T1 object at 0x7f502c104290>
>>> T2()
T2 (T2) was instantiated
the new instance is <__main__.T2 object at 0x7f502c0f7ed0>
<__main__.T2 object at 0x7f502c0f7ed0>
>>> T3()
T3 (T2) was instantiated
the new instance is <__main__.T3 object at 0x7f502c104290>
T3 (T3) was instantiated
the new instance is <__main__.T3 object at 0x7f502c104290>
<__main__.T3 object at 0x7f502c104290>
对于 3:是,来自__call__,如上所示。
【讨论】:
__init__ 时,推荐的方法是什么? (故意不称这个“构造函数”,因为它在元编程上下文中很重要)