mypy: isinstance-condition中的函数定义:为什么条件中的类型没有缩小?



我在基于变量类型的条件中定义函数,这是其他类型的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没有抱怨这一点。我不明白它为什么要抱怨。
  • test0test1都调用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被推断为Callableis None类型保护,所以您以后不能分配None给它。您可以在test0test1(肮脏的方式)中使用assert callable(a)来修复此问题,或者使用与test2相同的模式。默认参数绑定(使用def test0(a: Callable = a)也应该是有效的,但mypy不喜欢它的某些原因-这是一个假阳性,但可能太难处理或开发人员现在没有时间。

如果你对mypy对此的看法感兴趣,请浏览这篇文章。这是一个可以在网上玩的游乐场。

最新更新