确定方法是否仍然是抽象的

时间:2017-09-13 09:59:27

标签: python python-decorators

我有一个带有abstractmethod和子类的基类来实现这个方法。如何在运行时确定对象(不检查对象类型或调用方法)该方法是否抽象?

class Base:
    @abc.abstractmethod
    def someAbstractMethod(self):
        raise NotImplementedError("Not implemented yet.")


class Subclass(Base):
    def someAbstractMethod(self):
        some_operations

objects = [Base(),Subclass(),Base(),Subclass(),Subclass(),...]
for object in objects:
    #here I want check if method is still abstract or not

2 个答案:

答案 0 :(得分:7)

Python阻止使用抽象方法为类创建实例。所以只是你有一个实例的事实意味着你没有抽象的方法。

但是,您必须使用ABCMeta metaclass来正确触发此行为:

class Base(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def someAbstractMethod(self):
        raise NotImplementedError("Not implemented yet.")

您也可以从abc.ABC继承,以通过基类获取相同的元类。

如果您想查看可能列出的抽象方法,请使用__abstractmethods__属性;它是一组仍然是抽象的名称:

>>> import abc
>>> class Base(metaclass=abc.ABCMeta):
...     @abc.abstractmethod
...     def someAbstractMethod(self):
...         raise NotImplementedError("Not implemented yet.")
...
>>> class Subclass(Base):
...     def someAbstractMethod(self):
...         some_operations
...
>>> Base.__abstractmethods__
frozenset({'someAbstractMethod'})
>>> Subclass.__abstractmethods__
frozenset()

@abc.abstractmethod装饰器所做的一切都是在函数对象上设置__isabstractmethod__属性;然后是元类使用这些属性。

因此,如果您深入挖掘或使用忘记使用ABCMeta元类的第三方库,则可以测试这些属性:

>>> class Base:    # note, no metaclass!
...     @abc.abstractmethod
...     def someAbstractMethod(self):
...         raise NotImplementedError("Not implemented yet.")
...
>>> getattr(Base().someAbstractMethod, '__isabstractmethod__', False)
True

如果你需要'修复'这些破碎的抽象基类,你需要子类并混合ABCMeta__abstractmethods__冻结集添加到你的类中继承自:

BaseClass.__abstractmethods__ = frozenset(
    name for name, attr in vars(BaseClass).items()
    if getattr(attr, '__isabstractmethod__', False))

class DerivedClass(BaseClass, metaclass=abc.ABCMeta):
    # ...

现在DerivedClass是一个正确的ABC派生类,可以正确跟踪和处理任何抽象方法。

答案 1 :(得分:4)

您在这里没有正确使用abc(因为它至少不打算使用)。您的Base类应该使用abc.ABCMeta作为元类,这将阻止实现不实现abstractmethods的子类的实例化。否则只使用abc.abstractmethod装饰器几乎没用。

Python 2.7:

class Base(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def someAbstractMethod(self):
        # no need to raise an exception here
        pass

Python 3.x

class Base(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def someAbstractMethod(self):
        # no need to raise an exception here
        pass