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
但 Abstract
和 B
都没有直接继承自 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
我有一个 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
但 Abstract
和 B
都没有直接继承自 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