考虑以下代码示例
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()
这正确地引发了错误:
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
但是,当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
当继承自C中定义的任何类(包括itertools.chain
和numpy.ndarray
但仍然正确地引发python中定义的类的错误时,似乎会发生这种情况。为什么实现其中一个内置类型会破坏抽象类的功能?
答案 0 :(得分:7)
令人惊讶的是,阻止实例化抽象类的测试发生在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);
(差不多?)所有不是object
的内置类型提供的不同__new__
覆盖object.__new__
而不会调用object.__new__
。当您从非object
内置类型多重继承时,继承其__new__
方法,绕过抽象方法检查。
我在__new__
文档中没有看到abc
或内置类型的多重继承。文档可以在这里使用增强功能。
他们使用元类进行ABC实现似乎有些奇怪,将其他元类与抽象类一起使用变得一团糟,然后将核心语言代码中的关键检查与{{无关1}}并运行抽象和非抽象类。
问题跟踪器上的这个问题有report,自2009年以来一直在萎缩。
答案 1 :(得分:4)
我问了一个类似的question,并根据user2357112 supports Monica的链接错误报告,提出了一种解决方法(基于Xiang Zhang的建议):
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()
这引发了TypeError: Can't instantiate abstract class Derived with abstract methods bar, foo
,这是原始行为。