为什么我可以在Python中调用抽象类方法?

4
如果我定义了一个抽象类,并且其中有一个抽象方法:
import abc


class A(abc.ABC):

    @classmethod
    @abc.abstractmethod
    def foo(cls):
        pass

我无法实例化它(正如预期的那样):
>>> A()
TypeError: Can't instantiate abstract class A with abstract methods foo

但是我可以调用它的抽象方法而没有错误。

>> A.foo()

这种行为有没有文档说明?

已在Python 3.6和3.7上测试。


2
Python官方文档关于ABC:docs.python.org/3/library/abc.html 信息:www.python-course.eu/python3_abstract_classes.php - CFV
3个回答

8

对此并没有明确的说明。

仅仅是abstractmethod装饰器的文档说:

如果一个类的元类是ABCMeta的派生类,除非重写所有抽象方法和属性,否则不能实例化它。

而PEP 3119则说:

包含至少一个声明了这个装饰器但尚未被重写的方法的类无法实例化。

稍后又说到

实现@abstractmethod装饰器将函数属性__isabstractmethod__设置为True。 ABCMeta.__new__方法将类型属性__abstractmethods__计算为具有值为true的__isabstractmethod__属性的所有方法名称的集合。它通过组合基类的__abstractmethods__属性,添加新类字典中所有具有真实__isabstractmethod__属性的方法的名称,并删除新类字典中没有真实__isabstractmethod__属性的方法的名称来完成此操作。如果生成的__abstractmethods__集合非空,则认为该类是抽象的,并且尝试实例化它将引发TypeError

我的理解是@abstractmethod从不阻止方法被调用,而只是说该类不能被实例化,并且除非覆盖了所有抽象方法,否则其子类仍将是抽象的。

因此,我不会说这是设计上的,但至少可以认为这是一种假定的副作用。


2

foo是一个classmethod,通过property,类方法可以直接由类本身调用。
通过这样做,

In [3]: A.foo()  

你并没有实例化类A,而是只是在类A上调用函数foo


2

继承 abc.ABC 表示类 A 不能直接实例化。

@abc.abstractmethod 装饰器会强制检查 A 的任何子类在类型/名称解析期间。如果 class subofA(A): 没有实现装饰的方法,则会引发异常。

一旦类型/名称解析通过,abstractmethod 装饰器不会阻止您调用该方法。毕竟,除非它是类方法,否则您无法在没有实例的情况下调用该方法。

通过同时使用 @classmethod@abstractmethod 装饰 foo,开发人员可以指定 A.foo() 可以安全地调用而无需实例化类,但任何子类 A 的人都必须实现一个重载方法以保留该行为。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接