通常在 Python 中,可以使用以下技術檢測子類中的方法是否已被覆寫:
>>> class Foo:
... def mymethod(self): pass
...
>>> class Bar(Foo): pass
...
>>> Bar.mymethod is Foo.mymethod
True
該運算式Bar.mymethod is Foo.mymethod將計算為True如果從方法Foo還沒有在被覆寫Bar,但會評估為False,如果該方法已經在被覆寫Bar。此技術適用于從 繼承的 dunder 方法object以及非 dunder 方法:
>>> Bar.__new__ is Foo.__new__
True
>>> Bar.__eq__ is Foo.__eq__
True
我們可以在一個函式中形式化這個邏輯,如下所示:
def method_has_been_overridden(superclass, subclass, method_name):
"""
Return `True` if the method with the name `method_name`
has been overridden in the subclass
or an intermediate class in the method resolution order
"""
if not issubclass(subclass, superclass):
raise ValueError(
"This function only makes sense if `subclass` is a subclass of `superclass`"
)
subclass_method = getattr(subclass, method_name)
if not callable(method):
raise ValueError(f"'{subclass.__name__}.{method_name}' is not a method")
return subclass_method is not getattr(superclass, method_name, object())
然而,當涉及到兩種方法時,這種技術失敗了:__init_subclass__和__subclasshook__:
>>> class Foo: pass
...
>>> class Bar(Foo): pass
...
>>> Bar.__init_subclass__ is Foo.__init_subclass__
False
>>> Bar.__subclasshook__ is Foo.__subclasshook__
False
而且,舉一個更令人困惑的例子:
>>> type.__init_subclass__ is type.__init_subclass__
False
我有兩個問題:
- Why does this technique fail with these methods, and these methods only? (I have not been able to find any other examples where this technique fails -- but if there are any, I'd love to know about them!)
- Is there an alternative technique that can be used to detect if
__init_subclass__or__subclasshook__have been defined in a subclass after being left undefined in the superclass?
uj5u.com熱心網友回復:
__init_subclass__特殊情況下是一個類方法,無論你是否裝飾它classmethod。就像每次通過類實體訪問屬性時Foo().mymethod回傳一個新method實體一樣,每次通過類本身訪問屬性時都會Foo.__init_subclass__生成一個新instance方法。
__subclasshook__另一方面,必須宣告為類方法才能正常作業。如果將其定義為簡單的函式/實體方法,則不會假定它是類方法。
uj5u.com熱心網友回復:
__init_subclass__并且__subclasshook__是類方法。正如你在這里看到的:
>>> Bar.__sizeof__
<method '__sizeof__' of 'object' objects>
>>> Bar.__eq__
<slot wrapper '__eq__' of 'object' objects>
>>> Bar.__subclasshook__
<built-in method __subclasshook__ of type object at 0x000002D70AAC5340>
>>> Bar.__init_subclass__
<built-in method __init_subclass__ of type object at 0x000002D70AAC5340>
>>> Foo.__init_subclass__
<built-in method __init_subclass__ of type object at 0x000002D70AACAF70>
>>>
__init_subclass__并__subclasshook__參考它們的不同實體的類,Bar的十六進制是0x000002D70AAC5340,而Foo的十六進制是0x000002D70AACAF70。
正如您在 的檔案中看到的那樣__init_subclass__,它說:
類方法 物件。
__init_subclass__(cls)
它說“類方法”。
uj5u.com熱心網友回復:
__init_subclass__是一種特殊的方法,因為classmethod即使您@classmethod在定義它時沒有對其進行修飾,它也是隱式的。然而,這里的問題并不是因為它__init_subclass__是一種特殊方法。相反,您用來檢測方法是否已在子類中被覆寫的技術存在根本性錯誤:它根本不適用于任何classmethods:
>>> class Foo:
... def mymethod(self): pass
... @classmethod
... def my_classmethod(cls): pass
...
>>> class Bar(Foo): pass
...
>>> Bar.mymethod is Foo.mymethod
True
>>> Bar.my_classmethod is Foo.my_classmethod
False
這是因為Python 中系結方法的作業方式:Python 中的方法是描述符。
觀察以下代碼行在實體方法方面的等效性。第一種(更正常的)呼叫mymethod實體的f方式只是第二種呼叫實體方法的語法糖f:
>>> class Foo:
... def mymethod(self):
... print('Instance method')
...
>>> f = Foo()
>>> f.mymethod()
Instance method
>>> Foo.__dict__['mymethod'].__get__(f, Foo)()
Instance method
每次呼叫__get__中的未系結方法都會Foo.__dict__產生一個新物件;只有通過訪問類上的實體方法,我們才能測驗身份,就像您在問題中所做的那樣。但是,對于classmethods,即使從類訪問方法也會呼叫__get__該方法:
>>> class Foo:
... @classmethod
... def my_classmethod(cls):
... print('Class method')
...
>>> Foo.my_classmethod is Foo.my_classmethod
False
>>> Foo.my_classmethod()
Class method
>>> Foo.__dict__['my_classmethod'].__get__(Foo, Foo)()
Class method
怎么樣__new__?
您的問題指出您現有的方法適用于__new__. 這很奇怪——我們剛剛確定這個方法不適用于classmethods,而且看起來__new__確實像. 畢竟,的第一個引數是命名的!但是,Python 檔案清楚地表明情況并非如此:classmethod__new__cls
object.__new__(cls[, ...])呼叫以創建 class 的新實體
cls。__new__()是一個靜態方法(特殊情況,所以你不需要這樣宣告),它將請求實體的類作為它的第一個引數。
這是一個staticmethod,不是一個classmethod!謎團已揭開。
檢測子類是否覆寫超類方法的更好方法
確定某個方法是否已在子類中被覆寫的唯一可靠方法是按__dict__方法決議順序遍歷每個類的 :
def method_has_been_overridden(superclass, subclass, method_name):
"""
Return `True` if the method with the name `method_name`
has been overridden in the subclass
or an intermediate class in the method resolution order
"""
if not issubclass(subclass, superclass):
raise ValueError(
"This function only makes sense if `subclass` is a subclass of `superclass`"
)
subclass_method = getattr(subclass, method_name)
if not callable(subclass_method):
raise ValueError(f"'{subclass.__name__}.{method_name}' is not a method")
for cls in subclass.__mro__:
if cls is superclass:
return False
if method_name in cls.__dict__:
return True
此函式可以正確確定__init_subclass__或任何其他classmethod是否已在子類中被覆寫:
>>> class Foo: pass
...
>>> class Bar(Foo): pass
...
>>> class Baz(Foo):
... def __init_subclass__(cls, *args, **kwargs):
... return super().__init_subclass__(*args, **kwargs)
>>> method_has_been_overridden(Foo, Bar, '__init_subclass__')
False
>>> method_has_been_overridden(Foo, Baz, '__init_subclass__')
True
非常感謝@chepner和U12-Forward,他們出色的回答幫助我解決了這個問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/343480.html
標籤:python oop inheritance
上一篇:從R中的資料幀串列進行子集化
下一篇:由于堆損壞,應用程式觸發了斷點
