[英]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.