在所有继承类中强制实现一个方法

Kor*_*rem 5 python abstract python-2.7

我有一种情况,我想强制从某个(抽象)类继承的每个类来实现一个方法。这是我通常会使用@abstractmethod 实现的目标。但是,考虑到这种多重继承的情况:

from abc import ABCMeta, abstractmethod
class A(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def very_specific_method(self):
        pass

class B(A):
    def very_specific_method(self):
        print 'doing something in B'

class C(B):
    pass
Run Code Online (Sandbox Code Playgroud)

我也想强制C实施该方法。我希望每个直接或间接继承 A 的类都被强制实现该方法。这可能吗?

澄清:我希望这适用于特定方法,而不是所有抽象方法。抽象方法应该继续以相同的方式工作,但也许应该创建一个新的装饰器来指示不同类型的方法。

旁注:我在问题中使用了 abc,因为这似乎与该问题最相关。我了解抽象方法通常如何工作并定期使用它们。这是一种不同的情况,我不介意不是通过 abc 完成的。

Ash*_*ary 3

ABCMeta的修改版本应该可以解决问题。

在这里,我们不是检查设置__isabstractmethod__True仅在基类中的方法,而是检查它是否在类的 MRO 中,如果在 MRO 中的任何类中找到它并且它不存在于当前类中,那么我们可以将其添加到放abstracts

from abc import ABCMeta, abstractmethod
from _weakrefset import WeakSet

class EditedABCMeta(ABCMeta):

    def __new__(mcls, name, bases, namespace):
        cls = type.__new__(mcls, name, bases, namespace)
        # Compute set of abstract method names
        abstracts = set(name
                     for name, value in namespace.items()
                     if getattr(value, "__isabstractmethod__", False))

        for base in cls.__mro__:
            for name, value in base.__dict__.items():
                if getattr(value, "__isabstractmethod__", False) and name not in cls.__dict__:
                    abstracts.add(name)

        cls.__abstractmethods__ = frozenset(abstracts)
        # Set up inheritance registry
        cls._abc_registry = WeakSet()
        cls._abc_cache = WeakSet()
        cls._abc_negative_cache = WeakSet()
        cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
        return cls

class A(object):
    __metaclass__ = EditedABCMeta

    @abstractmethod
    def veryspecificmethod(self):
        pass

class B(A):
    def veryspecificmethod(self):
        print 'doing something in B'

    @abstractmethod
    def foo(self):
        print 'foo from B'

class C(B):
    def foo(self):
        pass

class D(C, B):
    pass

if __name__ == '__main__':
    for cls in (C, D):
        try:
            cls().veryspecificmethod
        except TypeError as e:
            print e.message
    print '-'*20
    for cls in (C, D):
        try:
            cls().foo
        except TypeError as e:
            print e.message
Run Code Online (Sandbox Code Playgroud)

输出:

Can't instantiate abstract class C with abstract methods veryspecificmethod
Can't instantiate abstract class D with abstract methods foo, veryspecificmethod
--------------------
Can't instantiate abstract class C with abstract methods veryspecificmethod
Can't instantiate abstract class D with abstract methods foo, veryspecificmethod
Run Code Online (Sandbox Code Playgroud)

编辑:

添加一个特殊的装饰器@enforcedmethod可以满足您的要求而不影响@abstractmethod

from abc import ABCMeta, abstractmethod

def enforcedmethod(func):
    func.__enforcedmethod__ = True
    return func

class EditedABCMeta(ABCMeta):

    def __call__(cls, *args, **kwargs):

        enforcedmethods = set()
        for base in cls.__mro__:
            for name, value in base.__dict__.items():
                if getattr(value, "__enforcedmethod__", False) and name not in cls.__dict__:
                    enforcedmethods.add(name)
        if enforcedmethods:
            raise TypeError("Can't instantiate abstract class {} "
                            "with enforced methods {}".format(
                                cls.__name__, ', '.join(enforcedmethods)))
        else:
            return super(EditedABCMeta, cls).__call__(*args, **kwargs)

class A(object):
    __metaclass__ = EditedABCMeta

    @enforcedmethod
    def veryspecificmethod(self):
        pass
    @abstractmethod
    def simplemethod(self):
        pass

class B(A):
    def veryspecificmethod(self):
        print 'doing something in B'
    def simplemethod(self):
        pass

class C(B):
    pass

class D(C):
    def veryspecificmethod(self):
        print 'doing something in D'
Run Code Online (Sandbox Code Playgroud)

输出:

>>> D().veryspecificmethod()
doing something in D
>>> C().veryspecificmethod()

Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    C().veryspecificmethod()
  File "C:\Python27\so.py", line 19, in __call__
    cls.__name__, ', '.join(enforcedmethods)))
TypeError: Can't instantiate abstract class C with enforced methods veryspecificmethod
Run Code Online (Sandbox Code Playgroud)