agf*_*agf 22
直接回答你的问题是:当你想要做更多的不仅仅是定制实例创建,或当你想分开全班确实从它是如何创建的.
请参阅我在Python中创建单例的答案以及相关的讨论.
有几个优点.
它允许您将类的功能与创建它的详细信息分开.元类和类都负责一件事.
您可以在元类中编写一次代码,并使用它来自定义几个类的调用行为,而不必担心多重继承.
子类可以覆盖其__new__方法中的行为,但是__call__在元类上根本不需要调用__new__.
如果有设置工作,你可以__new__在元类的方法中执行它,它只发生一次,而不是每次调用类.
__new__如果您不担心单一责任原则,肯定有很多情况下定制工作也是如此.
但是,有些其他用例必须在创建类时更早发生,而不是在创建实例时发生.当它们发挥作用时,必须使用元类.请参阅Python中元类的(具体)用例是什么?有很多很好的例子.
unu*_*tbu 15
一个区别是,通过定义元类__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'>, (),{}
Run Code Online (Sandbox Code Playgroud)
请注意,SubFoo.__new__永远不会被调用.相反,如果您定义Foo.__new__没有元类,则允许子类重写Foo.__new__.
当然,你可以定义MetaFoo.__call__来打电话cls.__new__,但这取决于你.通过拒绝这样做,您可以防止子类__new__调用其方法.
我没有看到在这里使用元类的强大优势.而且由于"简单比复杂更好",我建议使用__new__.
Mic*_*oka 13
当您仔细观察这些方法的执行顺序时,细微差别会变得更加明显.
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)
Run Code Online (Sandbox Code Playgroud)
请注意,除了记录我们正在做的事情之外,上面的代码实际上并没有做任何事情.每种方法都遵循其父实现,即其默认值.所以除了记录它之外,就好像你只是简单地声明了如下内容:
class Meta_1(type): pass
class Class_1(object):
__metaclass__ = Meta_1
Run Code Online (Sandbox Code Playgroud)
现在让我们创建一个实例 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__()
Run Code Online (Sandbox Code Playgroud)
因此,如果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
Run Code Online (Sandbox Code Playgroud)
从上面的呼叫订单中注意到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)
Run Code Online (Sandbox Code Playgroud)
让我们观察一下重复尝试创建类型对象时会发生什么 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
Run Code Online (Sandbox Code Playgroud)
现在使用类' __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)
Run Code Online (Sandbox Code Playgroud)
请注意,即使在类上成功注册单例,上述实现也不会阻止__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
Run Code Online (Sandbox Code Playgroud)
我认为一个充实的 Python 3 版本的pyroscope 的答案可能很方便有人复制、粘贴和破解(可能是我,当我发现自己在 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')
Run Code Online (Sandbox Code Playgroud)
输出:
** 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
Run Code Online (Sandbox Code Playgroud)
同一篇文章强调的另一个重要资源是 David Beazley 的 PyCon 2013 Python 3 元编程教程。