今天我們來聊聊__call__
。希望透過今天的內容,我們更清楚 my_inst(...)
、MyClass(...)
及MyType(...)
或class MyClass(metaclass=MyType)
等語法背後的意義。
我們先記錄以下這段關於type.__call__
的論述,稍後再輔以範例說明。
type
預設實作有__call__
,當其被call
時,會呼叫所傳入type instance
的__new__
。如果其返回的是該type instance
的instance
時(稱作inst
),會再幫忙呼叫type instance
的__init__
,最後回傳inst
。type instance
可能為class
或其它繼承type
的metaclass
或是type
本身。
當生成instance
的class
有實作__call__
時,該instance
為callable
。
# 01
中,my_inst
因為MyClass
有實作__call__
,所以是callable
。
# 01
class MyClass:
def __call__(self):
print('MyClass __call__ called because of `my_inst()`')
if __name__ == '__main__':
my_inst = MyClass()
my_inst() # my_inst is callable
MyClass __call__ called because of `my_inst()`
當生成class
的metaclass
有實作__call__
時,該class
為callable
。# 02
中,MyClass
的metaclass
(MyType
)有實作__call__
,所以MyClass
為callable
。
當MyClass
被呼叫時,相當於呼叫MyType.__call__
,而其將此呼叫delegate
給super()__call__
。super()__call__
相當於super(MyType, cls)__call__
,也就是說會將cls
(即MyClass
)傳入type.__call__
(MyClass
是type
的instance
)。結合開頭對type.__call__
的描述,我們可以了解type.__call__
會幫忙呼叫MyClass.__new__
。由於MyClass.__new__
回傳了一個MyClass
的instance
,所以MyClass.__init__
也會被呼叫。
# 02
class MyType(type):
def __call__(cls, *args, **kwargs):
print('MyType __call__ called because of `MyClass()`')
instance = super().__call__(*args, **kwargs)
return instance
class MyClass(metaclass=MyType):
def __new__(cls):
print('MyClass __new__ called')
instance = super().__new__(cls)
return instance
def __init__(self):
print('MyClass __init__ called')
def __call__(self):
print('MyClass __call__ called because of `my_inst()`')
if __name__ == '__main__':
my_inst = MyClass() # MyClass is callable as well
my_inst() # my_inst is callable
MyType __call__ called because of `MyClass()`
MyClass __new__ called
MyClass __init__ called
MyClass __call__ called because of `my_inst()`
當生成metaclass
的metaclass
有實作__call__
時,該metaclass
(第一個metaclass
)為callable
。# 03
中,MyType
的metaclass
(type
)有實作__call__
,所以MyType
為callable
(這就是為什麼我們可以寫出MyType(...)
或class MyClass(metaclass=MyType)
這類語法的原因)。
當MyType
被呼叫時,相當於呼叫type.__call__
,此時MyType
會被傳入type.__call__
(MyType
是type
的instance
)。type.__call__
會幫忙呼叫MyType.__new__
。由於MyType.__new__
回傳了一個MyType
的instance
(即class
),所以MyType.__init__
也會被呼叫。
此處因為很容易出錯,所以需要再加強說明。MyType.__new__
及MyType.__init__
之所以被call
,並不是因為實作有MyType.__call__
,而是因為type
實作有__call__
。MyType.__call__
是為了生成其instance
而準備的(例如MyClass
),與MyType
是否為callable
沒有關係。
# 03
class MyType(type):
def __new__(mcls, cls_name, cls_bases, cls_dict, **kwargs):
print('MyType __new__ called')
cls = super().__new__(mcls, cls_name, cls_bases, cls_dict)
return cls
def __init__(cls, cls_name, cls_bases, cls_dict, **kwargs):
print('MyType __init__ called')
def __call__(cls, *args, **kwargs):
print('MyType __call__ called because of `MyClass()`')
instance = super().__call__(*args, **kwargs)
return instance
class MyClass(metaclass=MyType): # MyType is callable as well
def __new__(cls):
print('MyClass __new__ called')
instance = super().__new__(cls)
return instance
def __init__(self):
print('MyClass __init__ called')
def __call__(self):
print('MyClass __call__ called because of `my_inst()`')
if __name__ == '__main__':
my_inst = MyClass() # MyClass is callable as well
my_inst() # my_inst is callable
MyType __new__ called
MyType __init__ called
MyType __call__ called because of `MyClass()`
MyClass __new__ called
MyClass __init__ called
MyClass __call__ called because of `my_inst()`
type
的metaclass
還是type
,而type
實作有__call__
,所以type
為callable
。
當type
被call
時,相當於呼叫type
的metaclass
的__call__
也就是type.__call__
,此時type
會被傳入type.__call__
(type
是type
的instance
)。type.__call__
會負責生成一個type
的instance
(即class
)。
當一個obj
是callable
時,代表建立其的class
實作有__call__
。換句話說,class
實作的__call__
是為其instance
作準備,而不是自己。當class
本身也要是callable
時,代表建立其的metaclass
也要實作有__call__
。
如果對上一點很難理解的話,您可以將instance
、class
、MyType
等的生成過程,想成是不同層級的情況,幫助理解。
instance
是callable
時,代表建立其的class
實作有__call__
。換句話說,class
實作的__call__
是為其instance
作準備,而不是自己。當class
本身也要是callable
時,代表建立其的metaclass
也要實作有__call__
。class
是callable
時,代表建立其的metaclass
實作有__call__
。換句話說,metaclass
實作的__call__
是為其instance
(即class
)作準備,而不是自己。當metaclass
本身也要是callable
時,代表建立其的metaclass
(或是可以想成meta-metaclass
)也要實作有__call__
。有時候,可能會對__call__
到底會產生何種行為,會呼叫誰的__new__
和__init__
感到疑惑。此時關鍵是想清楚,究竟是誰在呼叫,是my_inst(...)
、MyClass(...)
、MyType(...)
或type(...)
中的哪一層。