确定Python类是抽象基类还是具体基类
我的Python应用程序包含许多抽象类和实现。例如:确定Python类是抽象基类还是具体基类,python,abstract-class,abc,Python,Abstract Class,Abc,我的Python应用程序包含许多抽象类和实现。例如: 导入abc 导入日期时间 类消息显示(对象): __元类_uu=abc.ABCMeta @抽象财产 def显示(自我,信息): 通过 类FriendlyMessageDisplay(消息显示): def问候(自我): 小时=datetime.datetime.now().timetuple().tm_小时 如果小时数小于7: 引发异常(“睡眠时无法打招呼”) elif小时
导入abc
导入日期时间
类消息显示(对象):
__元类_uu=abc.ABCMeta
@抽象财产
def显示(自我,信息):
通过
类FriendlyMessageDisplay(消息显示):
def问候(自我):
小时=datetime.datetime.now().timetuple().tm_小时
如果小时数小于7:
引发异常(“睡眠时无法打招呼”)
elif小时<12小时:
self.display(“早上好!”)
elif小时<18:
self.display(“下午好!”)
elif小时<20:
self.display(“晚上好!”)
其他:
self.display(“晚安”)
类FriendlyMessagePrinter(FriendlyMessageDisplay):
def显示(自我,信息):
打印(信息)
FriendlyMessagePrinter
是我们可以使用的一个具体类
FriendlyMessagePrinter().greet()
晚安。
…但是MessageDisplay
和FriendlyMessageDisplay
是抽象类,尝试实例化一个会导致错误:
TypeError:无法使用抽象方法实例化抽象类MessageDisplay
如何检查给定的类对象是否是(不可实例化的)抽象类?抽象类及其具体实现有一个
\uuuuuuAbstractMethods\uuuuu
属性,其中包含尚未实现的抽象方法和属性的名称。这种行为在以下章节中描述:
实现:修饰符将函数属性\uuuu isastractmethod\uuu
设置为值True
。ABCMeta.\uuuuu new\uuuuuu
方法将类型属性\uuuuuu abstractmethods\uuuuuu
计算为具有值为true的\uuuuu isabstractmethod\uuuuu
属性的所有方法名称的集合。它通过组合基类的\uuuu abstractmethods\uuuuuuuuuuu
属性,添加新类dict中所有具有true\uuuuuu isabstractmethod\uuuuuuuu
属性的方法的名称,并删除新类dict中所有不具有true\uuuuu isabstractmethod\uuuuuuuuu
属性的方法的名称来实现。如果生成的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。(如果这是在CPython中实现的,则可以使用内部标志Py\u TPFLAGS\u ABSTRACT
来加速此检查。)
所以在具体的类中,这个属性要么不存在,要么是一个空集。这很容易检查:
def是抽象的(cls):
如果不是hasattr(cls,“\uuuuu abstractmethods”):
return False#普通类
elif len(cls.\uuuuu abstractmethods\uuuuuuu)=0:
return False#抽象类的具体实现
其他:
返回True#抽象类
或者更简洁地说:
def is_abstract(cls):
return bool(getattr(cls, "__abstractmethods__", False))
print(是抽象(对象))#False
打印(是否为摘要(消息显示))#正确
打印(是否为摘要(FriendlyMessageDisplay))#正确
打印(是摘要(FriendlyMessagePrinter))#错误
这将检查类对象中是否设置了内部标志TPFLAGS\u IS\u ABSTRACT
,因此它不会像您的实现那样容易被愚弄:
class Fake:
__abstractmethods__ = 'bluh'
print(is_abstract(Fake), inspect.isabstract(Fake)) # True, False
您可以使用\u 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)
s/filepath/filename
。但这显然是错误的,对吗?对于一个没有方法的类,它会直接失败,如果我只是决定创建一个函数,比如def bluh(self):pass
。创造性的解决方案,但在我的整个项目中,这也会失败,因为几个抽象方法都有实现(通过super
调用),而不仅仅是pass
。对于普通读者,ABC:返回解释否决票:从数字导入数字;从进口商品检验;打印(isabstract(数字),数字.\uuuu abstractmethods\uuuuuuuuuu)
→ False,frozenset()
,尽管类型(编号)是abc.ABCMeta
(即True
)和Number
不可实例化。您的解决方案也遇到了同样的问题。至少在Python 3.7.6中,除非抽象类中至少有一个@abstractmethod
(或相关的decorator),否则这似乎不起作用。仅从ABC
继承或使用ABCMeta
类并不会为此目的使类抽象。
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)