Using the __call__ method of a metaclass instead of __new__?
在讨论元类时,docs声明:
You can of course also override other class methods (or add new
methods); for example defining a custom__call__() method in the
metaclass allows custom behavior when the class is called, e.g. not
always creating a new instance.
我的问题是:假设我希望在调用类时有自定义行为,例如缓存而不是创建新对象。我可以通过重写类的
对您的问题的直接回答是:当您想做的不仅仅是自定义实例创建,或者当您想将类的工作与它的创建方式分开时。
请参阅我在python中创建singleton的答案和相关讨论。
有几个优点。
它允许您将类的工作与创建它的详细信息分开。元类和类各自负责一件事。
您可以在一个元类中编写一次代码,并使用它定制几个类的调用行为,而不必担心多个继承。
子类可以覆盖它们的
如果有安装工作,您可以在元类的
当然,如果您不担心单一责任原则,那么定制
但是,还有其他用例必须在创建类时,而不是在创建实例时,更早地发生。正是在这些因素发挥作用的时候,元类才是必要的。看看您在Python中对元类的(具体)用例是什么?有很多很好的例子。
一个区别是,通过定义元类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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'>, (),{} |
注意,EDOCX1[5]从未被调用。相反,如果在没有元类的情况下定义
当然,您可以将
在这里,我看不到使用元类的显著优势。既然"简单胜于复杂",我建议使用
当您仔细观察这些方法的执行顺序时,细微的差异会变得更加明显。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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) |
注意,上面的代码除了记录我们正在做的事情之外,实际上不做任何事情。每个方法都遵从其父实现,即其默认实现。因此,除了日志记录,它实际上就像您简单地声明了如下内容:
1 2 3 | class Meta_1(type): pass class Class_1(object): __metaclass__ = Meta_1 |
现在让我们创建一个
1 2 3 4 5 6 | c = Class_1() # entering Meta_1.__call__() # entering Class_1.__new__() # exiting Class_1.__new__() # executing Class_1.__init__() # exiting Meta_1.__call__() |
因此,如果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
上述通知单中的通知,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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) |
让我们观察一下反复尝试创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
现在,使用类"EDOCX1"(27)方法来观察这个实现,以尝试完成相同的事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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) |
注意,上述实现即使在类上成功注册了一个singleton,也不会阻止调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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 |
这是生命周期阶段和您可以访问的内容的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
我认为一个完整的python 3版本的pyroscope的答案可能很方便有人复制、粘贴和破解(可能是我,当我发现自己在6个月后再次在这个页面上查找它时)。摘自本文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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') |
输出:
1 2 3 4 5 6 7 8 9 | ** 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 |
同一篇文章强调的另一个重要资源是DavidBeazley的pycon 2013 python 3元编程教程。