变量类型注解NameError不一致

Variable type annotation NameError inconsistency

在 Python 3.6 中,语言中引入了新的 Variable Annotations

但是,当类型不存在时,可能会发生两种不同的情况:

>>> def test():
...     a: something = 0
... 
>>> test()
>>> 
>>> a: something = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'something' is not defined

为什么不存在的类型处理行为不同?它不会导致人们忽略函数中未定义的类型吗?


备注

尝试使用 Python 3.6 RC1 和 RC2 - 相同的行为。

PyCharm 在函数内外都将 something 突出显示为 "unresolved reference"。

你可以去https://www.python.org/ftp/python/3.6.0/下载RC2版本来测试注释,但是wim说的发布版本还没有发布。然而,我下载并使用 Python3.6 解释器尝试了您的代码,没有出现任何错误。

局部变量(即函数内部)的行为至少记录在 Runtime Effects of Type Annotations:

部分

Annotating a local variable will cause the interpreter to treat it as a local, even if it was never assigned to. Annotations for local variables will not be evaluated:

def f():
    x: NonexistentName  # No error.

并继续解释全局变量的区别:

However, if it is at a module or class level, then the type will be evaluated:

x: NonexistentName  # Error!
class X:
    var: NonexistentName  # Error!

这种行为让我感到惊讶,所以我只能提供我对推理的猜测:如果我们将代码放在模块中,那么 Python 想要存储注释。

# typething.py
def test():
    a: something = 0

test()


something = ...

a: something = 0

然后导入:

>>> import typething
>>> typething.__annotations__
{'a': Ellipsis}
>>> typething.test.__annotations__
{}

为什么需要将它存储在模块对象上,而不是函数对象上 - 我还没有一个好的答案。也许是出于性能原因,因为注释是通过静态代码分析进行的,并且这些名称可能会动态更改:

...the value of having annotations available locally does not offset the cost of having to create and populate the annotations dictionary on every function call. Therefore annotations at function level are not evaluated and not stored.

最直接的答案(补充@wim 的答案)来自 issue tracker on Github 讨论提案的地方:

[..] Finally, locals. Here I think we should not store the types -- the value of having the annotations available locally is just not enough to offset the cost of creating and populating the dictionary on each function call.

In fact, I don't even think that the type expression should be evaluated during the function execution. So for example:

def side_effect():
    print("Hello world")
def foo():
    a: side_effect()
    a = 12
    return a
foo()

should not print anything. (A type checker would also complain that side_effect() is not a valid type.)

来自 BDFL 本人 :-) 既没有创建字典也没有执行评估。

目前,函数对象仅存储其定义中提供的注释:

def foo(a: int): 
    b: int = 0

get_type_hints(foo)   # from typing
{'a': <class 'int'>}

为本地 变量 注释创建另一个字典显然被认为成本太高。

你可以试试这样写:

>>>a: 'something' = 0