VSCode:使用非本地导致变量类型 "Never"
VSCode: Using nonlocal causes variable type "Never"
我发现 VSCode/Pylance 和一些 Python 代码有奇怪的行为。考虑以下最小示例:
#!/usr/bin/env python3
from typing import Optional
def main():
x: Optional[list] = None
def f():
nonlocal x
x = [10]
f()
assert isinstance(x, list)
y: int = x[0] + 3
assert isinstance(y, int)
print(y)
main()
对 f() 的调用显然将外部 x
设置为 [10]
。将鼠标悬停在第一个 assert
语句(在函数调用之后)的 x
上会显示 (variable) x: None
,这是可以的。但是在 assert
语句之后,将鼠标悬停在 x 上会显示 (variable) x: Never
。这会导致 x
在所有后续行中的语法突出显示和自动完成都失败,因为 VSCode/Pylance 显然看到了一个具有某种“无类型”的变量。输入 x.
甚至不会显示对象的内置成员,如 x.__class__
或 x.__doc__
。将 x
分配给另一个类型变量没有帮助。第一个断言导致 mypy 接受代码,而 PyLint 说 Value 'x' is unsubscriptable
,因此其他工具之间似乎也存在一些分歧。这有点烦人,因为代码可以正常工作。
问题:这是 Pylance 中的错误吗?甚至预期的行为?有什么办法可以强制 Pylance 查看 x
的正确类型?
工具版本:
$ code --version
1.56.2
054a9295330880ed74ceaedda236253b4f39a335
x64
$ pylint --version
pylint 2.8.2
astroid 2.5.6
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0]
$ mypy --version
mypy 0.800
VSCode Python: v2021.5.842923320
VSCode 皮兰斯:v2021.5.3
谢谢你和最诚挚的问候,
菲利普
编辑:在 GitHub
上发布了问题
虽然将鼠标悬停在 x
上显示 (variable) x: Never
,但在 Settings.json 中添加 "debug.inlineValues": true
然后 开始调试,可以看到x
是一个特殊的变量,它的属性__class__
确实是list
:
当设置 Jedi 为语言服务器时,悬停在 x 上是 NoneType
或 list
:
您可以在 github/pylance-release 中寻求更多帮助。
根据GitHub上的讨论,我将回答如下问题:
这是Pylance的bug吗?不是bug,但是Pyright(Pylance的类型检查器)中实现的类型推断没有考虑局部突变由外部执行流程引起(例如包含 nonlocal
的回调函数的 运行)。
甚至是预期的行为?是的,至少部分是,考虑到以上几点。
有没有办法强制Pylance看到x
的正确类型?是的,在[=之后使用x = cast(list, x)
14=].
使用 cast
,最小示例如下所示:
#!/usr/bin/env python3
from typing import Optional, cast
def main():
x: Optional[list] = None
def f():
nonlocal x
x = [10]
f()
assert isinstance(x, list)
x = cast(list, x)
y: int = x[0] + 3 # Now x is reported as '(variable) x: list'
assert isinstance(y, int)
print(y)
main()
进一步总结 GitHub 问题中的解释:行 x: Optional[list] = None
使 x
成为 None
类型,因为这就是它的实际情况,无论类型如何声明。现在,缺少对函数f
内部x
突变的分析,当到达assert isinstance(x, list)
行时,x
的类型仍然是None
。由于类型 None
不是 list
的实例,对于类型检查器来说,这一行永远不会通过。为了表明这一点,x
的类型因此设置为 Never
.
我发现 VSCode/Pylance 和一些 Python 代码有奇怪的行为。考虑以下最小示例:
#!/usr/bin/env python3
from typing import Optional
def main():
x: Optional[list] = None
def f():
nonlocal x
x = [10]
f()
assert isinstance(x, list)
y: int = x[0] + 3
assert isinstance(y, int)
print(y)
main()
对 f() 的调用显然将外部 x
设置为 [10]
。将鼠标悬停在第一个 assert
语句(在函数调用之后)的 x
上会显示 (variable) x: None
,这是可以的。但是在 assert
语句之后,将鼠标悬停在 x 上会显示 (variable) x: Never
。这会导致 x
在所有后续行中的语法突出显示和自动完成都失败,因为 VSCode/Pylance 显然看到了一个具有某种“无类型”的变量。输入 x.
甚至不会显示对象的内置成员,如 x.__class__
或 x.__doc__
。将 x
分配给另一个类型变量没有帮助。第一个断言导致 mypy 接受代码,而 PyLint 说 Value 'x' is unsubscriptable
,因此其他工具之间似乎也存在一些分歧。这有点烦人,因为代码可以正常工作。
问题:这是 Pylance 中的错误吗?甚至预期的行为?有什么办法可以强制 Pylance 查看 x
的正确类型?
工具版本:
$ code --version
1.56.2
054a9295330880ed74ceaedda236253b4f39a335
x64
$ pylint --version
pylint 2.8.2
astroid 2.5.6
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0]
$ mypy --version
mypy 0.800
VSCode Python: v2021.5.842923320
VSCode 皮兰斯:v2021.5.3
谢谢你和最诚挚的问候,
菲利普
编辑:在 GitHub
上发布了问题虽然将鼠标悬停在 x
上显示 (variable) x: Never
,但在 Settings.json 中添加 "debug.inlineValues": true
然后 开始调试,可以看到x
是一个特殊的变量,它的属性__class__
确实是list
:
当设置 Jedi 为语言服务器时,悬停在 x 上是 NoneType
或 list
:
您可以在 github/pylance-release 中寻求更多帮助。
根据GitHub上的讨论,我将回答如下问题:
这是Pylance的bug吗?不是bug,但是Pyright(Pylance的类型检查器)中实现的类型推断没有考虑局部突变由外部执行流程引起(例如包含
nonlocal
的回调函数的 运行)。甚至是预期的行为?是的,至少部分是,考虑到以上几点。
有没有办法强制Pylance看到
x
的正确类型?是的,在[=之后使用x = cast(list, x)
14=].
使用 cast
,最小示例如下所示:
#!/usr/bin/env python3
from typing import Optional, cast
def main():
x: Optional[list] = None
def f():
nonlocal x
x = [10]
f()
assert isinstance(x, list)
x = cast(list, x)
y: int = x[0] + 3 # Now x is reported as '(variable) x: list'
assert isinstance(y, int)
print(y)
main()
进一步总结 GitHub 问题中的解释:行 x: Optional[list] = None
使 x
成为 None
类型,因为这就是它的实际情况,无论类型如何声明。现在,缺少对函数f
内部x
突变的分析,当到达assert isinstance(x, list)
行时,x
的类型仍然是None
。由于类型 None
不是 list
的实例,对于类型检查器来说,这一行永远不会通过。为了表明这一点,x
的类型因此设置为 Never
.