python abstractmethod 与另一个基类破坏了抽象功能
Posted
技术标签:
【中文标题】python abstractmethod 与另一个基类破坏了抽象功能【英文标题】:python abstractmethod with another baseclass breaks abstract functionality 【发布时间】:2016-09-20 19:18:54 【问题描述】:考虑以下代码示例
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 中定义的类的错误。为什么实现一种内置类型会破坏抽象类的功能?
【问题讨论】:
@DonkeyKong(或其他任何不明白的人)方法foo
应该被强制在子类中被覆盖,通常(并且不从str
继承)实例化它会引发一个错误,但是当也从 str
继承时不会发生错误,并且抽象方法 test.foo
是一个有效的可调用方法。
@TadhgMcDonald-Jensen 刚刚了解,谢谢 :)
@Torxed str
不是变量名。
我才意识到,谢谢你让我读了两遍@DonkeyKong :)
哦,见鬼 - 如果有人进入源代码,我会很高兴。但这是规范的一部分吗?是否存在需要这种行为的情况?或者它是一个错误,或者只是没有被考虑的案例?
【参考方案1】:
我问了一个类似的question 并根据user2357112 supports Monicas 链接的错误报告,我想出了这个解决方法(基于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 methodsuffix 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
,这是原始行为。
【讨论】:
【参考方案2】:令人惊讶的是,阻止实例化抽象类的测试发生在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__
方法,绕过抽象方法检查。
我在abc
文档中看不到任何关于__new__
或内置类型的多重继承的信息。文档可以在此处使用增强功能。
他们为 ABC 实现使用元类似乎有点奇怪,将其他元类与抽象类一起使用变得一团糟,然后将关键检查放在与 @987654334 无关的核心语言代码中@ 并为抽象类和非抽象类运行。
自 2009 年以来一直处于低迷状态的问题跟踪器上有此问题的 report。
【讨论】:
以上是关于python abstractmethod 与另一个基类破坏了抽象功能的主要内容,如果未能解决你的问题,请参考以下文章
在python中,我如何对一列中每个值与另一列中的值发生的次数(多少行)建立矩阵?
Python2和Python3中@abstractmethod的用法
python基础-abstractmethod__属性propertysetterdeleterclassmethodstaticmethod