为啥在元类派生自ABCMeta的类中需要使用@abstractmethod?

Posted

技术标签:

【中文标题】为啥在元类派生自ABCMeta的类中需要使用@abstractmethod?【英文标题】:Why does @abstractmethod need to be used in a class whose metaclass is derived from ABCMeta?为什么在元类派生自ABCMeta的类中需要使用@abstractmethod? 【发布时间】:2017-05-01 01:02:18 【问题描述】:

PEP 3119 声明:

@abstractmethod 装饰器只能在类体内使用,并且只能用于元类(派生自)ABCMeta 的类。不支持向类动态添加抽象方法,或在方法或类创建后尝试修改其抽象状态。

但是,我无法解释为什么会这样。具体来说,在没有显式继承自ABCMeta 的类中仅使用@abstractmethod 时,我没有注意到行为上的差异。在下面这个简单的例子中,如果我理解正确的话,正确的做事方式应该是:

import six
from abc import ABCMeta
from abc import abstractmethod

class Base(six.with_metaclass(ABCMeta)):
    def __init__(self):
        print('Init abstract base')

    @abstractmethod
    def do_something(self):
        pass

class Subclass(Base):
    def __init__(self):
        super(Subclass, self).__init__()

    def do_something(self):
        print('Done.')

sub = Subclass()
sub.do_something()

但是,如果我让 Base 类简单地从 object 继承,并且只在需要时使用装饰器,我注意到行为没有任何变化。

from abc import abstractmethod

class Base(object):
    def __init__(self):
        print('Init abstract base')

    @abstractmethod
    def do_something(self):
        pass

class Subclass(Base):
    def __init__(self):
        super(Subclass, self).__init__()

    def do_something(self):
        print('Done.')

sub = Subclass()
sub.do_something()

我发现即使在更复杂的架构上也是如此,所以我想知道:后一种方法什么时候会失败?

【问题讨论】:

【参考方案1】:

您看不到任何区别,因为您的第一个子类确实实现了do_something 抽象方法。

在两个版本的子类中注释掉do_something 的定义,您会发现在第一种情况下,在尝试实例化子类时会得到TypeError - 您还会得到一个尝试实例化的实例第一个版本 Base 类本身 FWIW。在第二个版本中,您可以实例化两个类(因为它们是抽象的,所以这是不可能的)并调用抽象的 do_something 方法——这违背了 ABC 的要点之一。

您还会错过 ABCs FWIW 的许多其他有趣功能...

【讨论】:

您能否详细说明在这种情况下遗漏的其他相关功能?

以上是关于为啥在元类派生自ABCMeta的类中需要使用@abstractmethod?的主要内容,如果未能解决你的问题,请参考以下文章

3.1.18 元类的练习

反射元类 练习

为啥 Pylint 在元类定义的属性使用上出错?

元类对象

面向对象进阶6:元类

WndProc 的替代方案,因为在从组件派生的类中需要?