Python什么时候检查ABC的具体子类是否实现了所需的方法?

dud*_*r33 10 python abstract-class typeerror python-3.x

在尝试编写单元测试时,检查抽象基类的具体子类是否确实在实例化时引发了TypeError,如果没有实现其中一个必需的方法,我偶然发现了一些让我想知道何时检查是否定义了所需方法的东西通过具体的子类实际执行.

到目前为止,我会说:在实例化对象时,因为这是在运行程序时实际引发Exception的时间.

但请看这个片段:

import abc

class MyABC(abc.ABC):
    @abstractmethod
    def foo(self): pass

MyConcreteSubclass(MyABC):
    pass
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,尝试实例化MyConcreteSubclass会引发TypeError:

>>> MyConcreteSubclass()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-39-fbfc0708afa6> in <module>()
----> 1 t = MySubclass()

TypeError: Can't instantiate abstract class MySubclass with abstract methods foo
Run Code Online (Sandbox Code Playgroud)

但是如果我首先声明一个有效的子类然后删除此方法会让我感到惊讶,会发生什么:

class MyConcreteSubclass(MyABC):
    def foo(self):
        print("bar")

MyConcreteSubclass.foo
--> <function __main__.MyConcreteSubclass.foo(self)>


>>> t = MyConcreteSubclass()
>>> t.foo()
bar

>>> del MyConcreteSubclass.foo
>>> MyConcreteSubclass.foo
<function __main__.MyABC.foo(self)>

>>> t = MyConcreteSubclass()
>>> print(t.foo())
None
Run Code Online (Sandbox Code Playgroud)

这肯定不是我的预期.在删除后检查MyConcreteSubclass.foo时,我们看到通过方法解析顺序检索基类的Abstract方法,这与我们首先在具体子类中没有实现foo的行为相同.

但是在实例化之后,不会引发TypeError.所以我想知道,当解释器评估具体子类的主体时,检查是否已经执行了所需的方法吗?如果是这样,为什么只有当有人试图实例化子类时才会引发TypeErrors?

上面显示的测试是使用Python 3.6.5执行的.

use*_*ica 13

它发生在课堂创作时.在Python 3.7中,它在C中,在compute_abstract_methodsin中Modules/_abc.c,它被称为ABCMeta.__new__.

顺便提一下,文档确实提到了这一点

不支持动态地将抽象方法添加到类中,或者尝试在创建方法或类时修改其抽象状态.

  • @AndreasFinkler:通过删除`foo`实现,你试图动态地转换一个具体的类摘要.正如文档所说,这是不受支持的. (2认同)

aba*_*ert 7

user2357112的答案涵盖了这里的主要问题,但还有一个次要问题:

为什么只有当有人试图实例化子类时才会引发TypeErrors?

如果在TypeError早期阶段,在创建课程时提出,则无法创建ABC的层次结构:

class MyABC(abc.ABC):
    @abstractmethod
    def foo(self): pass

class MySecondABC(MyABC):
    @abstractmethod
    def bar(self): pass
Run Code Online (Sandbox Code Playgroud)

除非有人试图实例化,否则你不希望它引发TypeError因为MySecondABC没有定义.fooMySecondABC

如果仅对添加新抽象方法的类合法,该怎么办?然后就可以创建ABC层次结构,但是创建中间帮助程序类是不可能的:

class MyABCHelper(MySecondABC):
    def foo(self):
        return bar(self)*2
Run Code Online (Sandbox Code Playgroud)

(有关更实际的示例,请参阅collections.abc允许您MutableSequence通过仅定义18种方法中的7种来实现完整接口的类.)

你不会想要一个使这些定义非法的规则.