13

次のコード例を検討してください

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.chainnumpy.ndarrayPython で定義されたクラスでエラーが正しく発生します。組み込み型の 1 つを実装すると、抽象クラスの機能が損なわれるのはなぜですか?

4

2 に答える 2

4

私は同様の質問をし、user2357112 が 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、元の動作である が発生します。

于 2020-02-08T09:16:10.233 に答える