为什么@abstractmethod 需要在class 中使用,其metaclass 是从ABCMeta 派生的?

Why does @abstractmethod need to be used in a class whose metaclass is derived from ABCMeta?

PEP 3119 指出:

The @abstractmethod decorator should only be used inside a class body, and only for classes whose metaclass is (derived from) ABCMeta. Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported.

但是,我找不到原因的解释。具体来说,我没有注意到在未明确继承自 ABCMeta 的 class 中仅使用 @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 class 简单地继承自 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()

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

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

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

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