确定是否Python类是一个抽象基类或混凝土(Determine if a Python class

2019-07-18 10:26发布

我的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)

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

FriendlyMessagePrinter().greet()
Good night.

...但MessageDisplayFriendlyMessageDisplay是抽象类,并试图实例化一个会导致错误:

TypeError: Can't instantiate abstract class MessageDisplay with abstract methods say

我如何检查是否给定的类的对象是(uninstantiatable)抽象类?

Answer 1:

import inspect
print(inspect.isabstract(object))                  # False
print(inspect.isabstract(MessageDisplay))          # True
print(inspect.isabstract(FriendlyMessageDisplay))  # True
print(inspect.isabstract(FriendlyMessagePrinter))  # False

这就验证了内部标志TPFLAGS_IS_ABSTRACT在类对象设置,所以它不能被作为你的执行情况容易上当:

class Fake:
    __abstractmethods__ = 'bluh'

print(is_abstract(Fake), inspect.isabstract(Fake)) # True, False


Answer 2:

抽象类和其具体的实现有一个__abstractmethods__包含尚未实施的抽象方法和属性的名称属性。 这种行为是在描述PEP 3199 :

实现: @abstractmethod装饰设置功能属性__isabstractmethod__的价值True 。 该ABCMeta.__new__方法计算类型属性__abstractmethods__作为集有所有的方法名__isabstractmethod__属性的值是true。 它通过结合这是否__abstractmethods__基类的属性,将在新的类字典具有真所有方法的名称__isabstractmethod__在新的类字典属性,并移除所有方法的名称不具有真正的__isabstractmethod__属性。 如果得到__abstractmethods__集非空,则该类被视为抽象的,并尝试实例将筹集类型错误。 (如果这是在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

或更简洁:

def is_abstract(cls):
    return bool(getattr(cls, "__abstractmethods__", False))
print(is_abstract(object))                 # False
print(is_abstract(MessageDisplay))         # True
print(is_abstract(FriendlyMessageDisplay)) # True
print(is_abstract(FriendlyMessagePrinter)) # False


Answer 3:

你可以用做_ast模块。 例如,如果你的示例代码是在foo.py ,你可以调用这个函数"foo.py""FriendlyMessagePrinter"作为参数。

def is_abstract(filepath, class_name):
    astnode = compile(open(filename).read(), filename, 'exec', _ast.PyCF_ONLY_AST)
    for node in astnode.body:
        if isinstance(node, _ast.ClassDef) and node.name == class_name:
            for funcdef in node.body:
                if isinstance(funcdef, _ast.FunctionDef):
                    if any(not isinstance(n, _ast.Pass) for n in funcdef.body):
                        return False
            return True
    print 'class %s not found in file %s' %(class_name, filepath)


文章来源: Determine if a Python class is an Abstract Base Class or Concrete