所以我正在阅读PEP 563——推迟注释评估,其中写道:
在Python 3.10中,函数和变量注释将不再在定义时评估。相反,字符串形式将保留在相应的
字典中。 静态类型检查器在行为上不会发现任何差异,而在运行时使用注释的工具将必须执行推迟的评估。__annotations__
这让我想知道,如果类型检查器实现了这个 PEP 的建议,这是否意味着
def f(foo: str):
pass
相当于这个形式?
def f(foo):
pass
f.__annotations__ = {"foo": "str"}
我测试了mypy、PyCharm和pytype,都不尊重动态添加的
__annotations__
。所以我的问题是:
我的理解是否正确,类型检查器最终会支持它,还是我误解了 PEP?
PEP 563 是关于注释的“推迟评估”。这与 注释是否存储在 __annotations__
属性内无关。注释已经并将存储在
__annotations__
中,只是存储格式发生了变化。句子
静态类型检查器不会发现行为上的差异never实际上与您的想法相反:类型检查器
已经检查了__annotations__
属性,因此不会看到任何差异。
f
和
f.__annotations__
:def f(foo: str): ...
print(f.__annotations__)
在 Python 3.9 或更早版本中,将打印:
{'foo': <class 'str'>}
使用
from __future__ import annotations
启用 PEP 563,将打印:
{'foo': 'str'}
换句话说,
__annotations__
值的类型发生了变化,但注释本身没有显示任何差异。由于类型检查器检查实际注释,而不是
__annotations__
属性,因此它们“也不会看到行为上的差异”。def f(foo: 'bar') -> 'baz':
...
当您获得无法解析的循环引用时,这是有用/必要的:
class Foo:
def bar() -> Foo: # doesn't resolve, Foo not defined yet
...
def baz() -> 'Foo': # "workaround"
...
from __future__ import annotations
可以透明地实现这一点,即即使在写入
-> Foo
时,它也会被评估为 -> 'Foo'
。此行为将成为 3.10 中的默认行为。类型检查器不会看到任何差异,因为它们必须已经处理了这种行为。
类型检查器不会动态评估
__annotations__
,因为这意味着它们需要实际
执行代码,这显然会产生副作用,因此对于static类型检查器来说是疯狂的。