繁体   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