我在基于变量类型的条件中定义函数,这是其他类型的Union。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对象(None和Sequence都是不可调用的)。
在函数定义之外,问题不会出现。
一些想法:
callable_a
只能是一个可调用的,所以我更愿意接受mymyy没有抱怨这一点。我不明白它为什么要抱怨。test0
和test1
都调用a
,区别只是参数。为什么这对mymyy有影响,特别是当a
的签名未定义时?
如果它有区别,我运行我的0.950。
这是正确的行为,在mypy
文档中有很好的解释。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
不喜欢它的某些原因-这是一个假阳性,但可能太难处理或开发人员现在没有时间。
如果你对mypy
对此的看法感兴趣,请浏览这篇文章。这是一个可以在网上玩的游乐场。