IAR*_*ARI 7 python metaclass multiple-inheritance abstract-methods
请考虑以下代码示例
import abc
class ABCtest(abc.ABC):
@abc.abstractmethod
def foo(self):
raise RuntimeError("Abstract method was called, this should be impossible")
class ABCtest_B(ABCtest):
pass
test = ABCtest_B()
Run Code Online (Sandbox Code Playgroud)
这正确地引发了错误:
Traceback (most recent call last):
File "/.../test.py", line 10, in <module>
test = ABCtest_B()
TypeError: Can't instantiate abstract class ABCtest_B with abstract methods foo
Run Code Online (Sandbox Code Playgroud)
但是当子类ABCtest也继承自内置类型str或list没有错误并test.foo()调用抽象方法时:
class ABCtest_C(ABCtest, str):
pass
>>> test = ABCtest_C()
>>> test.foo()
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
test.foo()
File "/.../test.py", line 5, in foo
raise RuntimeError("Abstract method was called, this should be impossible")
RuntimeError: Abstract method was called, this should be impossible
Run Code Online (Sandbox Code Playgroud)
这似乎从C中定义的任何类继承包括何时发生itertools.chain和numpy.ndarray,但仍引起了正确使用Python定义类的错误.为什么实现其中一个内置类型会破坏抽象类的功能?
令人惊讶的是,阻止实例化抽象类的测试发生在object.__new__,而不是abc模块本身定义的任何内容:
static PyObject *
object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
...
if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) {
...
PyErr_Format(PyExc_TypeError,
"Can't instantiate abstract class %s "
"with abstract methods %U",
type->tp_name,
joined);
Run Code Online (Sandbox Code Playgroud)
(几乎?)所有内置类型不object提供不同的__new__覆盖object.__new__ 和不调用object.__new__.当您从非object内置类型多重继承时,将继承其__new__方法,绕过抽象方法检查.
我没有看到文档中__new__内置类型的任何内容或多重继承abc.文档可以在这里使用增强功能.
他们使用元类用于ABC实现,使用其他元类与抽象类一样混乱,然后将核心语言代码中的关键检查与之无关,abc并运行两个抽象类似乎有点奇怪.和非抽象类.
关于问题跟踪器上的这个问题的报告自2009年以来一直在萎缩.
我问了一个类似的问题,并基于user2357112 支持 Monica的链接错误报告,我想出了这个解决方法(基于张翔的建议):
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def foo(self):
pass
@abstractmethod
def bar(self):
pass
def __new__(cls, *args, **kwargs):
abstractmethods = getattr(cls, '__abstractmethods__', None)
if abstractmethods:
msg = "Can't instantiate abstract class {name} with abstract method{suffix} {methods}"
suffix = 's' if len(abstractmethods) > 1 else ''
raise TypeError(msg.format(name=cls.__name__, suffix=suffix, methods=', '.join(abstractmethods)))
return super().__new__(cls, *args, **kwargs)
class Derived(Base, tuple):
pass
Derived()
Run Code Online (Sandbox Code Playgroud)
这引发了TypeError: Can't instantiate abstract class Derived with abstract methods bar, foo,这是原始行为。