Python 元类:为什么方法不从基类继承?
我试图理解Python中的元类黑魔法。例如,元类可以用来确保在派生类中实现某些方法,但我有一个问题。似乎我需要显式地实现所有必需的派生方法,即使没有理由(?)这样做 看看这个:Python 元类:为什么方法不从基类继承?,python,python-3.x,metaclass,Python,Python 3.x,Metaclass,我试图理解Python中的元类黑魔法。例如,元类可以用来确保在派生类中实现某些方法,但我有一个问题。似乎我需要显式地实现所有必需的派生方法,即使没有理由(?)这样做 看看这个: ~ $ 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
~ $ 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
这是预期的吗?如果是的话,您能给我一些提示如何修复这种行为,这样我就不需要重新实现方法了吗?继承不是将定义注入类的东西。相反,如果属性没有在本地定义,它允许在立即类之外继续查找属性 在没有元类的情况下,调用
Defined()。某些方法()
大致如下:
的实例
some\u method
的属性将查找该实例的\u dict\u
属性,但找不到该属性某些方法
在派生中查找,但未找到
在基于的派生类中的类的属性中搜索一些方法
,从基开始
Base.\uuu dict\uuu['some\u method']
被找到,并调用其值
将Derived
的实例作为其隐式第一个参数对于您的元类,
派生的
是使用其基类使用的相同元类创建的,意思是元。
(不是类型。
)用于创建派生的
。由于some\u方法
不在class语句的主体中,因此它不会出现在传递给Meta.\uuu new\uuu
的dict
对象中,从而引发AttributeError
。(请注意,由于您尚未尝试访问任何特定属性,因此应提高未实现,而不是属性错误。正文包含您正在定义的类中定义的内容。在您的例子中,派生的
中没有定义某个方法
,它不存在于其中
当您使用Derived().some_method()
(甚至Derived.some_method
)时,它将执行查找。它不会在派生类中找到它,但是它会使用MRO(方法解析顺序)尝试父类,也就是基类
查看def\uu new\uuu(cls,name,base,body)中的参数:
,在MRO开始之前,这些基与body
是分开的,后者是您在类中定义的。您似乎在试图重新设计模块及其@abstractmethod
装饰器。我不知道你为什么要这样做,但如果你这样做,你应该看看(这是从文档链接)
您的解决方案检查所需的名称是否在类的主体中实现。对于继承的方法来说,这是不正确的,但您似乎知道这一点,所以我不会解释。你想知道的是:你能做什么?您需要显式检查继承的方法
abc
的方法非常简单:在检查是否实现了所有抽象方法之前,它在每个基上调用getattr
。(模拟类上的getattr
操作的最佳方法是调用getattr
。)在那里找到的任何东西都不需要出现在主体中
使用静态单一必需方法而不是必须从装饰器输出中获取的动态方法集的用例更简单:
if (not any(hasattr(base, 'some_method') for base in bases)
and 'some_method' not in body):
看起来你只是想重现已经解决的问题(并且解决得更好),所以…为什么不直接使用它们呢?即使你不想使用它,你也可以看看。请注意,它解决了“我如何做getattr
所做的事情”的问题,只需在基底上调用getattr
。@babygame0ver你在说什么Derived()
确实创建了Derived
类的一个实例。Meta
似乎重复了ABCMeta
这一事实有些离题,这就是为什么Derived
似乎没有从其父类继承某个方法的原因。@chepner我认为这根本没有离题,因为这是一个非常可靠的提示,告诉你在哪里可以找到这个问题的好答案。我写了一个解释它的答案,但我认为stdlib作为这个问题的官方解决方案做这件事的事实远比互联网上的一些人(甚至我)说它有效的事实更有用和权威。你的示例代码不适用于grand grand children@λ用户父类已经构建好了。当您对它调用getattr
时,您可以看到它的父属性,包括方法,以及它的“祖父母”,等等。@abarnert我不知道这个模块!这正是我想要实现的,谢谢。这解释了为什么非元类示例有效,但它只简要介绍了元类示例无效的原因,然后没有给出任何解决方法的提示,这就是OP(我认为)实际寻找的。也就是说,它非常好地解释了非元类示例(简洁,但在所有相关细节上仍然完整,并且在不相关的细节上没有太多欺骗),因此我认为这仍然值得作为一个答案。@chepner那么我应该怎么做才能使我的示例起作用呢?我应该实现派生。_unew_u()
?如何实现?不,您应该查看abc
模块以了解它是如何实现的。也就是说,Meta.\uuuu new.\uuuu
应该查看基中的每个类
以及它们是否实现了某些方法
,如果某些方法
未在本地定义。(这种检查需要是递归的,因为它们可能都没有定义某个方法
if (not any(hasattr(base, 'some_method') for base in bases)
and 'some_method' not in body):