变量类型注释NameError不一致

时间:2016-12-22 21:28:18

标签: python python-3.x annotations python-3.6

在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为“未解析的引用”。

4 个答案:

答案 0 :(得分:14)

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

  

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

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

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

  

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

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__
{}

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

  

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

答案 1 :(得分:7)

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

  

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

     

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

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

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

来自BDFL本人:-)也不是创建的词典,也不是正在进行的评估。

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

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

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

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

答案 2 :(得分:4)

您可以转到https://www.python.org/ftp/python/3.6.0/并下载RC2版本以测试注释,但发布的版本如wim所说尚未发布。然而,我确实使用Python3.6解释器下载并尝试了您的代码,并且没有出现任何错误。

答案 3 :(得分:-1)

您可以尝试这样写:

>>>a: 'something' = 0