如何在Python函数作用域内获取类型注释?

问题描述 投票:0回答:3

例如:

def test():
    a: int
    b: str
    print(__annotations__)
test()

此函数调用会引发

NameError: name '__annotations__' is not defined
错误。

我想要的是在函数中获取类型注释

test
,就像全局范围或类范围中注释的返回字典一样。

有什么方法可以实现这一点吗?

如果不可能,为什么会存在这种语法?

python python-3.x annotations type-hinting
3个回答
15
投票

在函数内,局部变量的注释不会被保留,因此无法在函数内访问。只有模块和类级别的变量注释才会导致附加__annotations__对象。

来自

PEP 526规范

注释局部变量将导致解释器将其视为局部变量,即使它从未被分配。局部变量的注释将不会被评估[.]

[...]

此外,在模块或类级别,如果被注释的项是一个简单的名称,那么它和注释将存储在该模块或类的

__annotations__

属性中[.]


仅当定义了
实际模块级注释

时才设置

__annotations__
全局; 数据模型表明它是可选的:

模块

[...]
预定义(可写)属性:[...]__annotations__(可选)是一个字典,包含模块体执行期间收集的变量注释;

[...]
.

定义后,您可以从模块内的函数或通过

globals() 函数

访问它。
如果您在

class

语句内的函数中尝试此操作,那么就知道类主体命名空间

不属于嵌套函数范围的一部分
:

类块中定义的名称范围仅限于该类块;它不会扩展到方法的代码块 - 这包括推导式和生成器表达式,因为它们是使用函数作用域实现的。

您可以通过对类的引用来访问类名称空间。您可以通过使用类全局名称或内部绑定方法(通过
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()函数

将这些注释转换为类型对象。


0
投票

import re def fn(q: int): a: int = 1 def get_types(fn): source = inspect.getsource(fn) var_tps = re.findall(" +([a-z0-9]+) *?: *([a-z0-9]+) *=", source) return var_tps get_types(fn) # [('a', 'int')]



0
投票

import inspect import re def get_types_annotations(fn): source = inspect.getsource(fn) print(source) var_tps = { k: v for k, v in re.findall(r"(\w+) ?: ?([\w\"\[\], ']+[^ ]) ?=", source) } return var_tps

	
© www.soinside.com 2019 - 2024. All rights reserved.