变量类型注释NameError不一致

ale*_*cxe 21 python annotations python-3.x python-3.6

在Python 3.6中,新的变量注释在该语言中引入.

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

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


笔记

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

PyCharm something在函数内部和外部都突出显示为"未解析的引用".

wim*_*wim 14

局部变量(即函数内部)的行为至少在类型注释的运行时效果部分中记录:

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

def f():
    x: NonexistentName  # No error.
Run Code Online (Sandbox Code Playgroud)

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

但是,如果它处于模块级别或类级别,则将评估该类型:

x: NonexistentName  # Error!
class X:
    var: NonexistentName  # Error!
Run Code Online (Sandbox Code Playgroud)

这种行为对我来说似乎很令人惊讶,所以我只能提出我的猜测:如果我们将代码放在一个模块中,那么Python想要存储注释.

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

test()


something = ...

a: something = 0
Run Code Online (Sandbox Code Playgroud)

然后导入它:

>>> import typething
>>> typething.__annotations__
{'a': Ellipsis}
>>> typething.test.__annotations__
{}
Run Code Online (Sandbox Code Playgroud)

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

...本地可用注释的值不会抵消在每个函数调用上创建和填充注释字典的成本.因此,不评估功能级别的注释,也不进行存储.


Jim*_*ard 7

最直接的答案(补充@ wim的答案)来自Github上的问题跟踪器,其中讨论了提案:

[..]最后,当地人.在这里,我认为我们不应该存储类型 - 在本地使用注释的值不足以抵消在每个函数调用上创建和填充字典的成本.

实际上,我甚至不认为在函数执行期间应该计算类型表达式.例如:

def side_effect():
    print("Hello world")
def foo():
    a: side_effect()
    a = 12
    return a
foo()
Run Code Online (Sandbox Code Playgroud)

不应该打印任何东西.(类型检查器也会抱怨它side_effect()不是有效类型.)

来自BDFL本人:-)也没有创建的词典,也没有进行评估.

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

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

get_type_hints(foo)   # from typing
{'a': <class 'int'>}
Run Code Online (Sandbox Code Playgroud)

为局部变量注释创建另一个字典显然被认为太昂贵了.