當討論元類,the docs狀態:使用元類的__call__方法而不是__new__?
你當然也可以覆蓋其他類中的方法(或增加新 方法);例如在 元類中定義一個自定義的
__call__()
方法允許調用該類時的自定義行爲,例如,不是 總是創建一個新的實例。
我的問題是:假設我想在調用類時有自定義行爲,例如緩存而不是創建新對象。我可以通過重寫該類的__new__
方法來做到這一點。我什麼時候想用__call__
來定義元類呢?這種方法給了什麼是不可能的__new__
?
當討論元類,the docs狀態:使用元類的__call__方法而不是__new__?
你當然也可以覆蓋其他類中的方法(或增加新 方法);例如在 元類中定義一個自定義的
__call__()
方法允許調用該類時的自定義行爲,例如,不是 總是創建一個新的實例。
我的問題是:假設我想在調用類時有自定義行爲,例如緩存而不是創建新對象。我可以通過重寫該類的__new__
方法來做到這一點。我什麼時候想用__call__
來定義元類呢?這種方法給了什麼是不可能的__new__
?
直接回答你的問題是:當你想要做更不僅僅是定製實例創建,或當你想分開一下類確實從它是如何創建的。
請參閱我的回答Creating a singleton in Python及相關討論。
有幾個優點。
它可以讓你分開什麼類確實從它是如何創建的細節。元類和類分別負責一件事情。
您可以在元類中編寫一次代碼,並使用它來定製幾個類的調用行爲,而不用擔心多重繼承。
子類可以覆蓋它們的__new__
方法中的行爲,但元類上的__call__
根本不需要甚至調用__new__
。
如果有設置工作,可以在元類的__new__
方法中完成,它只發生一次,而不是每次調用該類。
如果您不擔心單一職責原則,定製__new__
的工作方式當然也很多。
但還有其他用例必須在創建類時更早發生,而不是在創建實例時發生。當這些元素出現時,元類是必需的。請參閱What are your (concrete) use-cases for metaclasses in Python?瞭解很多很棒的例子。
這是一個生命週期階段和您有權訪問的問題。 __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
一個區別是,通過定義元類__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__
。
還要注意,如果'MetaFoo .__ call __()'方法調用'super(MetaFoo,cls).__ call __(* args,** kwargs)','cls .__ new __()'將被間接調用。 – martineau
當您仔細觀察這些方法的執行順序時,細微的差異會變得更加明顯。
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
現在就讓如果type
是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.__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
來自call的通知orde r以上(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
不!元類上的'__new__'發生在創建_class_時,而不是_instance_。'__new__'發生在沒有元類的情況下'__new__'發生。 – agf
我在哪裏說'__new__'與實例創建有關? – pyroscope
我實際上在詢問類的'__new__',而不是元類的'__new__'。 –