它允许您将类的动作与创建类的细节分离。元类和类各自负责一件事。
您可以在元类中编写一次代码,并在多个类的调用行为中使用它,而无需担心多重继承。
子类可以在其__new__
方法中覆盖行为,但元类上的__call__
甚至不必调用__new__
。
如果有设置工作,您可以在元类的__new__
方法中执行此操作,它仅发生一次,而不是每次调用类时都会执行。
__new__
同样可行。当仔细观察这些方法的执行顺序时,微小的差异就会变得更加明显。
class Meta_1(type):
def __call__(cls, *a, **kw):
print "entering Meta_1.__call__()"
rv = super(Meta_1, cls).__call__(*a, **kw)
print "exiting Meta_1.__call__()"
return rv
class Class_1(object):
__metaclass__ = Meta_1
def __new__(cls, *a, **kw):
print "entering Class_1.__new__()"
rv = super(Class_1, cls).__new__(cls, *a, **kw)
print "exiting Class_1.__new__()"
return rv
def __init__(self, *a, **kw):
print "executing Class_1.__init__()"
super(Class_1,self).__init__(*a, **kw)
请注意,上面的代码实际上除了记录我们在做什么之外,并没有执行任何操作。每个方法都延迟到其父实现,即其默认实现。因此,除了记录日志之外,它实际上就像您只是声明了以下内容:
class Meta_1(type): pass
class Class_1(object):
__metaclass__ = Meta_1
现在让我们创建一个Class_1
的实例
c = Class_1()
# entering Meta_1.__call__()
# entering Class_1.__new__()
# exiting Class_1.__new__()
# executing Class_1.__init__()
# exiting Meta_1.__call__()
因此,如果type
是Meta_1
的父类,我们可以将type.__call__()
的伪实现想象为以下内容:class type:
def __call__(cls, *args, **kwarg):
# ... a few things could possibly be done to cls here... maybe... or maybe not...
# then we call cls.__new__() to get a new object
obj = cls.__new__(cls, *args, **kwargs)
# ... a few things done to obj here... maybe... or not...
# then we call obj.__init__()
obj.__init__(*args, **kwargs)
# ... maybe a few more things done to obj here
# then we return obj
return obj
注意从调用顺序中可以看出,Meta_1.__call__()
(或在此情况下为type.__call__()
)有机会影响是否最终调用Class_1.__new__()
和Class_1.__init__()
。在其执行过程中,Meta_1.__call__()
可以返回一个甚至没有被任何一个函数调用的对象。例如,考虑单例模式的实现:
class Meta_2(type):
__Class_2_singleton__ = None
def __call__(cls, *a, **kw):
# if the singleton isn't present, create and register it
if not Meta_2.__Class_2_singleton__:
print "entering Meta_2.__call__()"
Meta_2.__Class_2_singleton__ = super(Meta_2, cls).__call__(*a, **kw)
print "exiting Meta_2.__call__()"
else:
print ("Class_2 singleton returning from Meta_2.__call__(), "
"super(Meta_2, cls).__call__() skipped")
# return singleton instance
return Meta_2.__Class_2_singleton__
class Class_2(object):
__metaclass__ = Meta_2
def __new__(cls, *a, **kw):
print "entering Class_2.__new__()"
rv = super(Class_2, cls).__new__(cls, *a, **kw)
print "exiting Class_2.__new__()"
return rv
def __init__(self, *a, **kw):
print "executing Class_2.__init__()"
super(Class_2, self).__init__(*a, **kw)
让我们观察反复尝试创建 Class_2
类型对象时会发生什么。
a = Class_2()
# entering Meta_2.__call__()
# entering Class_2.__new__()
# exiting Class_2.__new__()
# executing Class_2.__init__()
# exiting Meta_2.__call__()
b = Class_2()
# Class_2 singleton returning from Meta_2.__call__(), super(Meta_2, cls).__call__() skipped
c = Class_2()
# Class_2 singleton returning from Meta_2.__call__(), super(Meta_2, cls).__call__() skipped
print a is b is c
True
现在观察一下使用类的 __new__()
方法来尝试实现相同功能的实现。
import random
class Class_3(object):
__Class_3_singleton__ = None
def __new__(cls, *a, **kw):
# if singleton not present create and save it
if not Class_3.__Class_3_singleton__:
print "entering Class_3.__new__()"
Class_3.__Class_3_singleton__ = rv = super(Class_3, cls).__new__(cls, *a, **kw)
rv.random1 = random.random()
rv.random2 = random.random()
print "exiting Class_3.__new__()"
else:
print ("Class_3 singleton returning from Class_3.__new__(), "
"super(Class_3, cls).__new__() skipped")
return Class_3.__Class_3_singleton__
def __init__(self, *a, **kw):
print "executing Class_3.__init__()"
print "random1 is still {random1}".format(random1=self.random1)
# unfortunately if self.__init__() has some property altering actions
# they will affect our singleton each time we try to create an instance
self.random2 = random.random()
print "random2 is now {random2}".format(random2=self.random2)
super(Class_3, self).__init__(*a, **kw)
请注意,尽管上述实现成功地在类上注册了单例,但无法阻止调用__init__()
,这是由于type.__call__()
隐式执行的(如果未指定元类,则type
为默认元类)。这可能会导致一些不良影响:
a = Class_3()
# entering Class_3.__new__()
# exiting Class_3.__new__()
# executing Class_3.__init__()
# random1 is still 0.282724600824
# random2 is now 0.739298365475
b = Class_3()
# Class_3 singleton returning from Class_3.__new__(), super(Class_3, cls).__new__() skipped
# executing Class_3.__init__()
# random1 is still 0.282724600824
# random2 is now 0.247361634396
c = Class_3()
# Class_3 singleton returning from Class_3.__new__(), super(Class_3, cls).__new__() skipped
# executing Class_3.__init__()
# random1 is still 0.282724600824
# random2 is now 0.436144427555
d = Class_3()
# Class_3 singleton returning from Class_3.__new__(), super(Class_3, cls).__new__() skipped
# executing Class_3.__init__()
# random1 is still 0.282724600824
# random2 is now 0.167298405242
print a is b is c is d
# True
Meta_1.__call__
中,你有 rv = super(Meta_1, cls).__call__(*a, **kw)
。你能解释一下为什么 Meta_1
是 super
中的第一个参数吗? - Kid_Learning_Csuper(arg1,arg2)
会查找第二个输入参数的MRO以找到第一个输入参数,并返回下一个类。但是rv = super(Meta_1,cls).__call__(*a, **kw)
,第二个参数(cls
或Class_1
)的MRO不包含第一个输入参数(Meta_1
),你无法在Class_1
的MRO中找到Meta_1
。所以我不明白为什么要调用type.__call__(Class_1)
。这就是我提出问题的原因。 - Kid_Learning_C一个区别在于,通过定义元类的__call__
方法,您要求它在任何类或子类的__new__
方法被调用之前先被调用。
class MetaFoo(type):
def __call__(cls,*args,**kwargs):
print('MetaFoo: {c},{a},{k}'.format(c=cls,a=args,k=kwargs))
class Foo(object):
__metaclass__=MetaFoo
class SubFoo(Foo):
def __new__(self,*args,**kwargs):
# This never gets called
print('Foo.__new__: {a},{k}'.format(a=args,k=kwargs))
sub=SubFoo()
foo=Foo()
# MetaFoo: <class '__main__.SubFoo'>, (),{}
# MetaFoo: <class '__main__.Foo'>, (),{}
注意到SubFoo.__new__
从未被调用。相反,如果你定义了没有元类的Foo.__new__
,那么你允许子类重写Foo.__new__
。
当然,你可以定义MetaFoo.__call__
来调用cls.__new__
,但这取决于你。通过拒绝这样做,你可以防止子类调用其__new__
方法。
我不认为在这里使用元类有明显的优势。而且,“简单比复杂好”,我建议使用__new__
。
MetaFoo.__call__()
方法调用了super(MetaFoo, cls).__call__(*args,**kwargs)
,那么间接调用cls.__new __()
。 - martineauclass Simple1(object, metaclass = SimpleMeta1):
...谢谢https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html#the-metaclass-hook-in-python-3 - ThorSummoner我认为对pyroscope答案进行深入阐述的Python 3版本可能会对某些人有用,可以复制、粘贴和修改(可能是我,在6个月后再次查看此页面时)。它来源于这篇文章:
class Meta(type):
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
print(' Meta.__prepare__(mcs=%s, name=%r, bases=%s, **%s)' % (
mcs, name, bases, kwargs
))
return {}
def __new__(mcs, name, bases, attrs, **kwargs):
print(' Meta.__new__(mcs=%s, name=%r, bases=%s, attrs=[%s], **%s)' % (
mcs, name, bases, ', '.join(attrs), kwargs
))
return super().__new__(mcs, name, bases, attrs)
def __init__(cls, name, bases, attrs, **kwargs):
print(' Meta.__init__(cls=%s, name=%r, bases=%s, attrs=[%s], **%s)' % (
cls, name, bases, ', '.join(attrs), kwargs
))
super().__init__(name, bases, attrs)
def __call__(cls, *args, **kwargs):
print(' Meta.__call__(cls=%s, args=%s, kwargs=%s)' % (
cls, args, kwargs
))
return super().__call__(*args, **kwargs)
print('** Meta class declared')
class Class(metaclass=Meta, extra=1):
def __new__(cls, myarg):
print(' Class.__new__(cls=%s, myarg=%s)' % (
cls, myarg
))
return super().__new__(cls)
def __init__(self, myarg):
print(' Class.__init__(self=%s, myarg=%s)' % (
self, myarg
))
self.myarg = myarg
super().__init__()
def __str__(self):
return "<instance of Class; myargs=%s>" % (
getattr(self, 'myarg', 'MISSING'),
)
print('** Class declared')
Class(1)
print('** Class instantiated')
输出:
** Meta class declared
Meta.__prepare__(mcs=<class '__main__.Meta'>, name='Class', bases=(), **{'extra': 1})
Meta.__new__(mcs=<class '__main__.Meta'>, name='Class', bases=(), attrs=[__module__, __qualname__, __new__, __init__, __str__, __classcell__], **{'extra': 1})
Meta.__init__(cls=<class '__main__.Class'>, name='Class', bases=(), attrs=[__module__, __qualname__, __new__, __init__, __str__, __classcell__], **{'extra': 1})
** Class declared
Meta.__call__(cls=<class '__main__.Class'>, args=(1,), kwargs={})
Class.__new__(cls=<class '__main__.Class'>, myarg=1)
Class.__init__(self=<instance of Class; myargs=MISSING>, myarg=1)
** Class instantiated
同一篇文章强调的另一个很棒的资源是David Beazley的PyCon 2013 Python 3元编程教程。
这是生命周期阶段和你所能访问的内容的问题。__call__
在__new__
之后被调用,并传递初始化参数,然后再传递给__init__
,因此您可以操纵它们。尝试运行以下代码并研究其输出:
class Meta(type):
def __new__(cls, name, bases, newattrs):
print "new: %r %r %r %r" % (cls, name, bases, newattrs,)
return super(Meta, cls).__new__(cls, name, bases, newattrs)
def __call__(self, *args, **kw):
print "call: %r %r %r" % (self, args, kw)
return super(Meta, self).__call__(*args, **kw)
class Foo:
__metaclass__ = Meta
def __init__(self, *args, **kw):
print "init: %r %r %r" % (self, args, kw)
f = Foo('bar')
print "main: %r" % f
__new__
发生在创建_class_时,而不是实例。当没有元类时,__call__
会在__new__
发生时发生。 - agf__new__
与实例创建有关? - pyroscope__new__
пјҢиҖҢдёҚжҳҜе…ғзұ»зҡ„__new__
гҖӮ - Eli Bendersky__new__
而不是元类的__new__
。 - agf__new __()
。有时您甚至需要元类__prepare__
方法更早地介入实例化过程。这篇文章是一个相当好的概述,尽管它可能需要一个应用实例。 - Chris__call__
比在类中覆盖__new__
更好。如果缓存的目的是为了提高效率,那么缓存 __new__
的结果是不理想的,因为无论如何都会执行 __init__
(数据模型:基本定制)。例如:
from functools import lru_cache
class MyClass:
@lru_cache(maxsize=None)
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __init__(self, ...)
"总是被执行。即使在缓存命中时也是如此。"
仅当 __init__
对已初始化的实例没有明显影响时,缓存 __new__
的结果才是可行的。否则,在缓存对象上执行 __init__
可能会对其其他引用产生烦人的副作用。
在元类级别进行缓存既避免了 1. 的性能问题,也避免了 2. 的正确性问题。例如:
from functools import lru_cache
class CachedInstances(type):
@lru_cache(maxsize=None)
def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)
class MyClass(metaclass=CachedInstances):
def __init__(self, ...)
"仅在缓存未命中时执行。"