python - 如何在 python 函数范围内获取类型注释?
问题描述
例如:
def test():
a: int
b: str
print(__annotations__)
test()
此函数调用引发NameError: name '__annotations__' is not defined
错误。
我想要的是在函数中获取类型注释test
,比如在全局范围或类范围内返回的注释字典。
它有什么方法可以做到这一点?
如果不可能,为什么存在这种语法?
解决方案
在函数中,局部变量的注释不会保留,因此无法在函数中访问。只有模块和类级别的变量注释才会导致__annotations__
附加对象。
注释局部变量将导致解释器将其视为局部变量,即使它从未被分配给。不会评估局部变量的注释[.]
[...]
此外,在模块或类级别,如果被注释的项目是一个简单的名称,那么它和注释将存储在该
__annotations__
模块或类的属性中[.]
仅当定义了实际的模块级注释__annotations__
时才设置全局;数据模型声明它是可选的:
模块
[...]预定义(可写)属性:[...] ;__annotations__
(可选)是包含在模块主体执行期间收集的变量注释的字典;[...]。
定义后,您可以从模块内的函数或通过globals()
函数访问它。
如果您在语句内的函数中尝试此class
操作,则要知道类主体名称空间不属于嵌套函数的范围:
类块中定义的名称范围仅限于类块;它没有扩展到方法的代码块——这包括理解和生成器表达式,因为它们是使用函数范围实现的。
相反,您可以通过对类的引用来访问类命名空间。您可以通过使用类全局名称或内部绑定方法(via type(self)
)、内部类方法(通过cls
参数)来获取此类引用。就ClassObject.__annotations__
在这种情况下使用。
如果您必须有权访问函数本地主体中的注释,则需要自己解析源代码。Python AST确实保留了本地注释:
>>> import ast
>>> mod = ast.parse("def foo():\n a: int = 0")
>>> print(ast.dump(mod.body[0], indent=4))
FunctionDef(
name='foo',
args=arguments(
posonlyargs=[],
args=[],
kwonlyargs=[],
kw_defaults=[],
defaults=[]),
body=[
AnnAssign(
target=Name(id='a', ctx=Store()),
annotation=Name(id='int', ctx=Load()),
value=Constant(value=0),
simple=1)],
decorator_list=[])
上面显示了带有单个注释的函数体的文本表示;该AnnAssign
节点告诉我们,它a
被注释为int
。您可以使用以下方法收集此类注释:
import inspect
import ast
class AnnotationsCollector(ast.NodeVisitor):
"""Collects AnnAssign nodes for 'simple' annotation assignments"""
def __init__(self):
self.annotations = {}
def visit_AnnAssign(self, node):
if node.simple:
# 'simple' == a single name, not an attribute or subscription.
# we can therefore count on `node.target.id` to exist. This is
# the same criteria used for module and class-level variable
# annotations.
self.annotations[node.target.id] = node.annotation
def function_local_annotations(func):
"""Return a mapping of name to string annotations for function locals
Python does not retain PEP 526 "variable: annotation" variable annotations
within a function body, as local variables do not have a lifetime beyond
the local namespace. This function extracts the mapping from functions that
have source code available.
"""
source = inspect.getsource(func)
mod = ast.parse(source)
assert mod.body and isinstance(mod.body[0], (ast.FunctionDef, ast.AsyncFunctionDef))
collector = AnnotationsCollector()
collector.visit(mod.body[0])
return {
name: ast.get_source_segment(source, node)
for name, node in collector.annotations.items()
}
上面的 walker 查找AnnAssignment
函数对象的源代码中的所有注解(因此需要有可用的源文件),然后使用 AST 源代码行和列信息来提取注解源。
鉴于您的测试功能,以上产生:
>>> function_local_annotations(test)
{'a': 'int', 'b': 'str'}
类型提示没有解析,它们只是字符串,因此您仍然必须使用该typing.get_type_hints()
函数将这些注释转换为类型对象。
推荐阅读
- amazon-web-services - TestCafe 与 SAM Local 一起使用,但在 SAM Deploy 之后不使用
- java - Weblogic 12c 部署错误,加载程序约束违规:解析字段“DATETIME”时
- flutter - 如何更改 Future.delayed() 持续时间,或避免这样做
- vue.js - stencil.js 组件的自定义字体
- java - 在 Spring Web 应用程序上跟踪用户活动
- intershop - 带参数的管道不会调用
- vue.js - 如何在 Cypress 中创建基于角色的测试?
- postgresql - 使用继承进行元数据迁移是否安全?
- android - Okhttp4,无法访问“正文”:它在“响应”中是包私有的
- javascript - 为什么第二个 then in promise 更早起作用?