Python确定类是否是抽象的(ABC),而不使用abstractmethod

2024-03-28 19:46:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个从^{}继承的类,但没有任何abstractmethod

我想检查一下它是否是一个抽象类,我现在很难办

Determine if a Python class is an Abstract Base Class or Concrete使用inspect.isabstract进行处方。但是,这仅在使用了abstractmethod时有效

如何检测类是否直接从ABC继承,而不使用inspect.isabstract


测试用例

# I need this to be flagged as abstract
class AbstractBaseClassNoAbsMethod(ABC):
    pass


# This is currently flaggable with `inspect.isabstract`
class AbstractBaseClassWithAbsMethod(ABC):
    @abstractmethod
    def some_method(self):
        """Abstract method."""


# I need this to be flagged as not abstract
class ChildClassFromNoAbsMethod(AbstractBaseClassNoAbsMethod):
    pass

我考虑过使用issubclass(some_class, ABC),但这是True,即使对于上面的ChildClassFromNoAbsMethod


当前最佳解决方案

我目前最好的解决方案是使用__bases__。这基本上只是列出父类,请参见How to get the parents of a Python class?

def my_isabstract(obj) -> bool:
    """Get if ABC is in the object's __bases__ attribute."""
    try:
        return ABC in obj.__bases__
    except AttributeError:
        return False

这是一个可行的解决办法。我不确定是否有更好/更标准的方法


Tags: toabstractifisasbeneedthis
2条回答

AbstractBaseClassNoAbsMethod不是抽象的。从ABC继承不会使类抽象inspect.isabstract正在产生正确的结果。您还将看到,如果尝试实例化AbstractBaseClassNoAbsMethod,而尝试实例化实际抽象类时会引发异常,则不会发生错误

如果您想测试一个类是否直接从abc.ABC继承,您可以使用__bases__执行您已经在执行的操作,但是许多抽象类不会从abc.ABC继承。例如,这是一个抽象类:

class Abstract(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def foo(self):
        pass

在这个例子中B也是如此:

class A(abc.ABC):
    @abc.abstractmethod
    def foo(self):
        pass

class B(A): pass

但是AbstractB都不是直接从abc.ABC继承的

事实上,即使使用ABCMeta作为元类,如果没有abstractmethods(或属性或属性),在Python中,类的所有用途都是而不是抽象的,因为它可以正常实例化

这就是为什么inspect.isabstract会返回False

如果您确实需要此检查,那么请检查类__bases__属性 ABC是一条路要走

即使如此,如果直接使用元类ABCMeta,该检查也会失败:

class MyClass(metaclass=ABCMeta):
   pass

现在,如果我们编写一些代码,将上面的MyClass标记为“抽象”,并将从它派生的另一个标记为“非抽象”,正如您在示例ChildClassFromNoAbsMethod中所希望的那样,那么从ABC派生的任何类都同样是“用ABCMeta元类声明的类的子类”

尽管如此,还是可以检查一个类在其元类层次结构中是否有ABCMeta,并一直检查它的祖先,以“查看”它是否是第一个使用ABCMeta作为其层次结构中元类的类,abc.ABC的直接子类。但正如你可能已经注意到的,它没有什么用处

您最好在yourAbstractBaseClassNoAbsMethod中有一个必须重写的一次性标记抽象属性,然后inspect.iabstract和防止实例化的语言机制都可以工作:


In [37]: class A(ABC): 
    ...:     marker = abstractmethod(lambda : None) 
    ...:                                                                                                                             

In [38]: inspect.isabstract(A)                                                                                                       
Out[38]: True

In [39]: class B(A): 
    ...:     marker = None 
    ...:                                                                                                                             

In [40]: B()                                                                                                                         
Out[40]: <__main__.B at 0x7f389bf19cd0>

In [41]: inspect.isabstract(B)                                                                                                       
Out[41]: False

相关问题 更多 >