Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
确定Python类是抽象基类还是具体基类_Python_Abstract Class_Abc - Fatal编程技术网

确定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小时

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

导入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)