Определите, является ли класс 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
. TheABCMeta.__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)