[英]Metaclasses: why method is not inherited from Base class?
我正在嘗試了解Python中的元類黑魔法 。 AFAIK可以使用元類,例如,以確保在派生類中實現某些方法,但是我有一個孫子輩 。 似乎即使沒有理由(?),我也必須顯式實現所有必需的派生方法。
看這個:
~ $ ipython
Python 3.6.5 (default, Apr 14 2018, 13:17:30)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.3.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: class Meta(type):
...: def __new__(cls, name, bases, body):
...: if 'some_method' not in body:
...: raise AttributeError('some_method should be implemented')
...: return super().__new__(cls, name, bases, body)
...:
...: class Base(metaclass=Meta):
...: def some_method(self):
...: print('Yay')
...:
...: class Derived(Base):
...: pass
...:
...:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-1-51c072cc7909> in <module>()
9 print('Yay')
10
---> 11 class Derived(Base):
12 pass
<ipython-input-1-51c072cc7909> in __new__(cls, name, bases, body)
2 def __new__(cls, name, bases, body):
3 if 'some_method' not in body:
----> 4 raise AttributeError('some_method should be implemented')
5 return super().__new__(cls, name, bases, body)
6
AttributeError: some_method should be implemented
據我了解,這不應該發生,因為some_method
應該像下面這樣從Base
類派生:
In [3]: class Base:
...: def some_method(self):
...: print('Yay')
...:
...: class Derived(Base):
...: pass
...:
...:
In [4]: Derived().some_method()
Yay
這是預期的嗎? 如果是的話,您能給我一些如何解決此問題的提示,這樣我就不需要重新實現方法了嗎?
您似乎正在嘗試重新發明abc
模塊及其@abstractmethod
裝飾器。 我不確定為什么要這樣做,但是如果這樣做,則應查看其來源 (從文檔中鏈接)。
您的解決方案將檢查所需的名稱是否在類的主體中實現。 對於繼承的方法來說,這不是正確的,但是您似乎知道這一點,因此我將不作解釋。 您想知道的是:您能做什么呢? 您需要顯式檢查繼承的方法。
abc
的方法非常簡單:在檢查是否實現了所有抽象方法之前,它會在每個base
上調用getattr
。 (畢竟,模擬getattr
將在類上執行的操作的最佳方法是調用getattr
。)在那里找到的任何內容都不需要出現在主體中。
您的用例(使用靜態的單個必需方法,而不是必須從裝飾器的輸出中獲取的動態方法集)用例更加簡單:
if (not any(hasattr(base, 'some_method') for base in bases)
and 'some_method' not in body):
繼承不是將定義注入類的東西。 相反,如果未在本地定義屬性,則它允許對屬性的查找超出直接類的范圍。
在沒有元類的情況下,對Defined().some_method()
的調用大致如下:
Defined
的實例。 __dict__
屬性的名為some_method
的屬性,但未找到。 Derived.__dict__
some_method
,但未找到。 Derived
的基於類的類的__dict__
屬性中搜索some_method
,從Base
開始。 Base.__dict__['some_method']
,並使用Derived
實例作為其隱式第一個參數來調用其值。 隨着你的元類, Derived
用其基類中使用的相同元類來創建,意Meta.__new__
(不type.__new__
)是用於創建Derived
。 由於some_method
不在類語句的主體中,因此它不會出現在傳遞給Meta.__new__
的dict
對象中,從而引發AttributeError
。 (請注意,由於您尚未嘗試訪問任何特定屬性,因此應引發NotImplemented
而不是AttributeError
。)
body
包含您要定義的類中定義的內容。 在您的情況下, some_method
沒有在Derived
定義,它不存在。
當您使用Derived().some_method()
(甚至Derived.some_method
)時,它將執行查找。 它不會在“派生”中找到它,但是隨后它將使用MRO(方法解析順序)嘗試父類(也稱為基類)。
請參閱def __new__(cls, name, bases, body):
在MRO啟動之前,bases與body
是分開的,這是您在類中定義的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.