Определите, является ли класс Python абстрактным базовым классом или конкретным

мое приложение 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.

...но!--5--> и FriendlyMessageDisplay являются абстрактными классами, и попытка создать экземпляр приведет к ошибке:

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

как я могу проверить, является ли данный объект класса (деинсталлируемым) абстрактным классом?

3 ответов


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, поэтому его нельзя обмануть так же легко, как вашу реализацию:

class Fake:
    __abstractmethods__ = 'bluh'

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

абстрактные классы и их конкретные реализации есть __abstractmethods__ атрибут, содержащий имена абстрактных методов и свойств, которые не были реализованы. Это поведение описано в PEP 3199:

реализация: на @abstractmethod декоратор устанавливает атрибут функции __isabstractmethod__ стоимостью True. The ABCMeta.__new__ метод вычисляет атрибут type __abstractmethods__ как набор всех имен методов, которые имеют __isabstractmethod__ атрибут, значение которого равно true. Он делает это путем объединения __abstractmethods__ атрибуты базовых классов, добавляя имена всех методов в новый класс dict, которые имеют true __isabstractmethod__ атрибут и удаление имен всех методов в новом классе dict, которые не имеют true


вы могли бы сделать это с помощью _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)