确定Python类是抽象基类还是混凝土

28 python abstract-class abc

我的Python应用程序包含许多抽象类和实现.例如:

import abc
import datetime

class MessageDisplay(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractproperty
    def display(self, message):
        pass

class FriendlyMessageDisplay(MessageDisplay):
    def greet(self):
        hour = datetime.datetime.now().timetuple().tm_hour

        if hour < 7:
            raise Exception("Cannot greet while asleep.")
        elif hour < 12:
            self.display("Good morning!")
        elif hour < 18:
            self.display("Good afternoon!")
        elif hour < 20:
            self.display("Good evening!")
        else:
            self.display("Good night.")

class FriendlyMessagePrinter(FriendlyMessageDisplay):
    def display(self, message):
        print(message)
Run Code Online (Sandbox Code Playgroud)

FriendlyMessagePrinter 是一个我们可以使用的具体类......

FriendlyMessagePrinter().greet()
Run Code Online (Sandbox Code Playgroud)
Good night.
Run Code Online (Sandbox Code Playgroud)

...但是MessageDisplay并且FriendlyMessageDisplay是抽象类并且尝试实例化一个会导致错误:

TypeError: Can't instantiate abstract class MessageDisplay with abstract methods say
Run Code Online (Sandbox Code Playgroud)

如何检查给定的类对象是否是(不可实例化的)抽象类?

mmg*_*mgp 27

import inspect
print(inspect.isabstract(object))                  # False
print(inspect.isabstract(MessageDisplay))          # True
print(inspect.isabstract(FriendlyMessageDisplay))  # True
print(inspect.isabstract(FriendlyMessagePrinter))  # False
Run Code Online (Sandbox Code Playgroud)

这会检查TPFLAGS_IS_ABSTRACT是否在类对象中设置了内部标志,因此不能像实现那样容易上当.

class Fake:
    __abstractmethods__ = 'bluh'

print(is_abstract(Fake), inspect.isabstract(Fake)) # True, False
Run Code Online (Sandbox Code Playgroud)

  • 至少在Python 3.7.6中,除非抽象类中至少有一个“@abstractmethod”(或相关的装饰器),否则这似乎不起作用。简单地继承“ABC”或使用“ABCMeta”类并不能为此目的创建一个类抽象。 (3认同)

小智 5

抽象类及其具体实现具有一个__abstractmethods__属性,其中包含尚未实现的抽象方法和属性的名称。PEP 3199中描述了此行为:

执行情况:@abstractmethod装饰设置功能属性__isabstractmethod__的值True。该ABCMeta.__new__方法将type属性计算__abstractmethods__为具有__isabstractmethod__属性值为true 的所有方法名称的集合。它通过组合__abstractmethods__基类的属性,添加新类dict中具有true __isabstractmethod__属性的所有方法的名称以及删除新类dict中不具有true __isabstractmethod__属性的所有方法的名称来实现此目的。如果结果__abstractmethods__集为非空,则该类被视为抽象类,并且尝试实例化该类将引发TypeError。(如果这是在CPython中实现的,则Py_TPFLAGS_ABSTRACT可以使用内部标志来加快此检查的速度。)

因此,在具体的类中,此属性将不存在或为空集。这很容易检查:

def is_abstract(cls):
    if not hasattr(cls, "__abstractmethods__"):
        return False # an ordinary class
    elif len(cls.__abstractmethods__) == 0:
        return False # a concrete implementation of an abstract class
    else:
        return True # an abstract class
Run Code Online (Sandbox Code Playgroud)

或更简洁地说:

def is_abstract(cls):
    return bool(getattr(cls, "__abstractmethods__", False))
Run Code Online (Sandbox Code Playgroud)
def is_abstract(cls):
    return bool(getattr(cls, "__abstractmethods__", False))
Run Code Online (Sandbox Code Playgroud)