为什么我们在python中使用抽象class时声明metaclass=abc.ABCMeta?

Why we declare metaclass=abc.ABCMeta when use abstract class in python?

我在网上看代码的时候遇到过以下使用abstract的情况类:

from abc import abstractmethod,ABCMeta
class Generator(object,metaclass=ABCMeta):
    @abstractmethod
    def generate(self):
        raise NotImplementedError("method not implemented")
generator=Generator()
generator.generate()

如预期的那样返回以下错误:

TypeError: Can't instantiate abstract class Generator with abstract methods generate

但是如果我这样写(唯一不同的是第二行)

from abc import abstractmethod,ABCMeta
class Generator(object):
    @abstractmethod
    def generate(self):
        raise NotImplementedError("method not implemented")
generator=Generator()
generator.generate()

虽然报错信息有变化,

NotImplementedError: method not implemented

我在实现generate方法时,Generator上面两种方式都正确执行了,

class GeneticAlgorithm(Generator):
    def generate(self):
        print("ABC")
ga=GeneticAlgorithm()
ga.generate()
>>> ABC

那么为什么我们需要语句 metaclass=ABCMeta

我从 GeeksforGeeks 那里了解到一些事情

ABCMeta metaclass provides a method called register method that can be invoked by its instance. By using this register method, any abstract base class can become an ancestor of any arbitrary concrete class.

但这还是没有让我理解声明metaclass=ABCMeta的必要性,感觉@abstractmethod修改方法就够了

第二个例子,

from abc import abstractmethod,ABCMeta
class Generator(object):
    @abstractmethod
    def generate(self):
        raise NotImplementedError("method not implemented")
generator=Generator()
generator.generate()  # HERE it raises

不以任何方式使用 @abstractclass 装饰器。它仅在调用 generate() 函数时引发 NotImplemetedError 异常,而在第一个示例中,在实例化 (generator=Generator()).

时引发错误

为了使用 @abstractmethod,class 必须有元 class ABCMeta(或者它必须继承自 class,例如ABC).

顺便说一句,有一些方法可以检查方法是否更快地实现(在 class 定义上),例如使用 __init_subclass__ 或通过定义自定义元 class并修改其 __new__ 方法。

您“需要”metaclass=ABCMeta 在实例化时执行规则

generator=Generator()  # Errors immediately when using ABCMeta
generator.generate()   # Only errors if and when you call generate otherwise

想象一下,如果 class 有几个抽象方法,只有其中一些在子对象中实现。它可能会工作很长一段时间,只有当你开始调用一个未实现的方法时才会出错。在依赖 ABC 之前急于失败通常是一件好事,同样的,函数引发异常通常比返回 None 来指示失败更好;您想在出现问题时立即知道,而不是在不知道错误的最终原因的情况下得到奇怪的错误。

Side-note:有一种比显式使用 metaclass=ABCMeta 语法更简洁的 ABC 方法:

from abc import abstractmethod, ABC

class Generator(ABC):

Python 几乎总是使用 metaclass 的空基 classes 来简化使用(特别是在 2 到 3 过渡时期,那里没有兼容的 metaclass 语法在两者中都有效,而直接继承是唯一有效的方法)。