【问题标题】:Python determine if class is abstract (ABC) without abstractmethodPython确定类是否是抽象的(ABC)没有abstractmethod
【发布时间】:2020-10-02 18:50:16
【问题描述】:

我有一个继承自ABC 的类,并且没有任何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

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

【问题讨论】:

    标签: python oop abc abstract-methods


    【解决方案1】:

    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

    【讨论】:

    • 我好像对抽象类有误解
    • Python 对抽象类的实现实际上非常奇特——因为检查只发生在运行时,并且在尝试实例化抽象类时发生。就传统的 OOP 文献而言,Python 根本没有“抽象类”。
    【解决方案2】:

    事实上,即使使用 ABCMeta 作为元类,如果没有抽象方法(或属性或属性),那么在 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
    

    【讨论】:

    • 感谢您为答案添加更多色彩,以及可能的解决方法@jsbueno!
    猜你喜欢
    • 2013-01-02
    • 2010-12-18
    • 2018-02-21
    • 1970-01-01
    • 2010-11-07
    • 2014-08-05
    • 2018-12-21
    • 2015-06-11
    • 1970-01-01
    相关资源
    最近更新 更多