mypy: isinstance-condition 中的函数定义:为什么条件中的类型没有缩小?
mypy: function definition in isinstance-condition: why is the type in the condition not narrowed down?
我正在基于变量类型(其他类型的联合)的条件内定义函数。 mypy 推断在这种情况下类型被缩小了,但似乎抛弃了这一知识。
我不明白为什么。这是 mypy 中的错误吗?
考虑一下:
from typing import Union, Callable
def test(a: Union[None, Callable]):
if a is None:
pass
else:
def test0():
a()
def test1(b: str):
a(b)
callable_a = a
def test2(b: str):
callable_a(b)
a()
a('foo')
mypy 的输出:
test.py:9: error: "None" not callable
Found 1 error in 1 file (checked 1 source file)
第 9 行是 test1 的正文。似乎这里应用了整个 Union[None, Callable]
(“整个联合”从这个例子中看不清楚,在实际代码中它是 Union[None, Sequence, Callable] 和 mypy objects None 和 Sequence 都不可调用)。
在函数定义之外,问题没有出现。
一些想法:
callable_a
只能是可调用的,所以我更愿意接受 mypy 不会抱怨这个。我不明白为什么它仍然在抱怨。
test0
和test1
都调用a
,区别仅在于参数。为什么这对 mypy 有影响,尤其是当 a
的签名未定义时?
我是 运行 mypy 0.950 如果它有所作为。
这是正确的行为,在 mypy
docs 中有很好的解释。 a
的推断是完全错误的,因为后期绑定。
这是文档中给出的示例:
from typing import Callable, Optional
def foo(x: Optional[int]) -> Callable[[], int]:
if x is None:
x = 5
print(x + 1) # mypy correctly deduces x must be an int here
def inner() -> int:
return x + 1 # but (correctly) complains about this line
x = None # because x could later be assigned None
return inner
inner = foo(5)
inner() # this will raise an error when called
现在谈谈您的代码。首先,不检查没有注释的函数,因此您看不到所有真正的错误。添加类型提示后,我们有以下内容:
from typing import Union, Callable
def test(a: Union[None, Callable]):
if a is None:
pass
else:
def test0() -> None:
a() # E: "None" not callable
def test1(b: str) -> None:
a(b) # E: "None" not callable
callable_a = a
reveal_type(callable_a) # N: Revealed type is "def (*Any, **Any) -> Any"
def test2(b: str) -> None:
callable_a(b)
a()
a('foo')
现在只有 test2
有效,这是预期的(见显示类型)。 callable_a
被推断为 Callable
只有 从 is None
类型保护,所以你不能稍后分配 None
给它。您可以使用 test0
和 test1
中的 assert callable(a)
(肮脏的方式)或使用与 test2
相同的模式来解决此问题。默认参数绑定(使用 def test0(a: Callable = a)
应该也是有效的,但 mypy
出于某种原因不喜欢它 - 这是一个误报,但可能太难处理或开发人员没有时间现在。
浏览 this issue if you are interested in mypy
opinion on that, and here's a playground 在线玩这个。
我正在基于变量类型(其他类型的联合)的条件内定义函数。 mypy 推断在这种情况下类型被缩小了,但似乎抛弃了这一知识。
我不明白为什么。这是 mypy 中的错误吗?
考虑一下:
from typing import Union, Callable
def test(a: Union[None, Callable]):
if a is None:
pass
else:
def test0():
a()
def test1(b: str):
a(b)
callable_a = a
def test2(b: str):
callable_a(b)
a()
a('foo')
mypy 的输出:
test.py:9: error: "None" not callable
Found 1 error in 1 file (checked 1 source file)
第 9 行是 test1 的正文。似乎这里应用了整个 Union[None, Callable]
(“整个联合”从这个例子中看不清楚,在实际代码中它是 Union[None, Sequence, Callable] 和 mypy objects None 和 Sequence 都不可调用)。
在函数定义之外,问题没有出现。
一些想法:
callable_a
只能是可调用的,所以我更愿意接受 mypy 不会抱怨这个。我不明白为什么它仍然在抱怨。test0
和test1
都调用a
,区别仅在于参数。为什么这对 mypy 有影响,尤其是当a
的签名未定义时?
我是 运行 mypy 0.950 如果它有所作为。
这是正确的行为,在 mypy
docs 中有很好的解释。 a
的推断是完全错误的,因为后期绑定。
这是文档中给出的示例:
from typing import Callable, Optional
def foo(x: Optional[int]) -> Callable[[], int]:
if x is None:
x = 5
print(x + 1) # mypy correctly deduces x must be an int here
def inner() -> int:
return x + 1 # but (correctly) complains about this line
x = None # because x could later be assigned None
return inner
inner = foo(5)
inner() # this will raise an error when called
现在谈谈您的代码。首先,不检查没有注释的函数,因此您看不到所有真正的错误。添加类型提示后,我们有以下内容:
from typing import Union, Callable
def test(a: Union[None, Callable]):
if a is None:
pass
else:
def test0() -> None:
a() # E: "None" not callable
def test1(b: str) -> None:
a(b) # E: "None" not callable
callable_a = a
reveal_type(callable_a) # N: Revealed type is "def (*Any, **Any) -> Any"
def test2(b: str) -> None:
callable_a(b)
a()
a('foo')
现在只有 test2
有效,这是预期的(见显示类型)。 callable_a
被推断为 Callable
只有 从 is None
类型保护,所以你不能稍后分配 None
给它。您可以使用 test0
和 test1
中的 assert callable(a)
(肮脏的方式)或使用与 test2
相同的模式来解决此问题。默认参数绑定(使用 def test0(a: Callable = a)
应该也是有效的,但 mypy
出于某种原因不喜欢它 - 这是一个误报,但可能太难处理或开发人员没有时间现在。
浏览 this issue if you are interested in mypy
opinion on that, and here's a playground 在线玩这个。