簡體   English   中英

元類:為什么方法不繼承自基類?

[英]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()的調用大致如下:

  1. 創建Defined的實例。
  2. 查找該實例的__dict__屬性的名為some_method的屬性,但未找到。
  3. 在“ Derived.__dict__ some_method ,但未找到。
  4. Derived的基於類的類的__dict__屬性中搜索some_method ,從Base開始。
  5. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM