Python 判断class 是否是没有abstractmethod 的abstract (ABC)

Python determine if class is abstract (ABC) without abstractmethod

我有一个 class 继承自 ABC,但没有任何 abstractmethod

我想检查它是否是抽象的 class,但我现在很困惑。

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

如何检测 class 是否直接继承自 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__。这基本上只是列出父级 classes,参见 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

这是一个可行的解决方案。我不确定那里是否有 better/more 标准方法。

AbstractBaseClassNoAbsMethod 不是抽象的。从 ABC 继承不会生成 class 摘要。 inspect.isabstract 产生了正确的结果。您还会看到,如果您尝试实例化 AbstractBaseClassNoAbsMethod 时不会发生任何错误,而尝试实例化实际抽象的 class 会引发异常。

如果你想测试一个 class 是否直接继承自 abc.ABC,你可以做你已经在用 __bases__ 做的事情,但是很多抽象 class es 不继承自 abc.ABC。例如,这是一个摘要 class:

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 作为元class,如果没有抽象方法(或属性或特性),class 也不是 在 Python 中用于所有目的的抽象,因为它可以正常实例化。

这就是为什么 inspect.isabstract 会 return False 就可以了。

如果你真的需要这个检查,那么,检查 class __bases__ 属性 ABC 是要走的路。

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

class MyClass(metaclass=ABCMeta):
   pass

现在,如果我们编写一些代码将上面的 MyClass 标记为 "Abstract" 并将从它派生的另一个代码标记为 "not abstract",正如您想要的示例 ChildClassFromNoAbsMethod,那么从 ABC 派生的任何 class 也将是 "subclass of a class that was declared with ABCMeta metaclass"。

仍然可以检查 class 是否在其元class 层次结构中具有 ABCMeta,并一直检查其祖先到 "see"如果它是第一个 class 在其层次结构中使用 ABCMeta 作为元 class,则 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