mypy:iSinstance条件中的函数定义:为什么条件中的类型未缩小?

发布于 2025-02-01 07:57:49 字数 969 浏览 3 评论 0 原文

我正在根据变量类型(其他类型的联合)在条件中定义功能。 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,collable] 是应用
(从这个示例中不清楚“整个联盟”,在实际代码中,它的联合[无,序列,可召唤]和没有序列的mypy对象是不可呼应的)。

外部功能定义,问题没有出现。

有些想法:

  • Callable_a 只能是一个可笑的人,因此我更愿意接受Mypy不会对此抱怨。我不明白为什么它在抱怨。
  • test0 test1 呼叫 a ,差异只是参数。为什么这会与Mypy有所不同,尤其是当 a 的签名不确定时?

如果有所不同,我正在运行MyPy 0.950。

I'm defining functions inside a condition based on a variables type, a Union of other types. mypy infers that the type is narrowed down in this context, but seems to throw this knowledge away.

I don't understand why. Is this a bug in mypy?

Consider this:

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's output:

test.py:9: error: "None" not callable

Found 1 error in 1 file (checked 1 source file)

line 9 is the body of test1. Seems that here the whole Union[None, Callable] is applied
("the whole union" is not clear from this example, in the actual code it's Union[None, Sequence, Callable] and mypy objects that both None and Sequence are not callable).

outside function definitions, the problem does not appear.

Some thoughts:

  • callable_a can only ever be a callable, so I'm a bit more willing to accept that mypy doesn't complain about this. I don't understand why it's complaining anyway.
  • both test0 and test1 call a, the difference is only the argument. Why does this make a difference to mypy, especially here when the signature of a is undefined?

I'm running mypy 0.950 if it makes a difference.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

面犯桃花 2025-02-08 07:57:49

这是正确的行为,在 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 none 键入pove,因此您不能分配 none none < /code>稍后。您可以使用 callable(a)内部 test0 test1 (肮脏的方式)解决此问题,或使用与 test2 。默认参数绑定(使用 def test0(a:callable = a)也应该有效,但 mypy 出于某种原因不喜欢它 - 它是误报,但是现在很难处理,或者现在没有时间

浏览 /a>如果您对 mypy 对此的看法有兴趣C67CB3D56A8E4BB2A718E6B346E0E7BA“ rel =“ nofollow noreferrer”>一个游乐场可以在线玩这个。

This is correct behavior which is well explained in the mypy docs. Inference of a would be simply wrong because of late binding.

Here's the example given in the docs:

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

Now about your code: First, functions without annotations are not checked, so you don't see all real errors. After adding type hints, we have the following:

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')

Now only test2 is valid, which is expected (see revealed type). callable_a is inferred to be Callable only from is None type guard, so you cannot assign None to it later. You can fix this with assert callable(a) inside test0 and test1 (dirty way) or use the same pattern as with test2. Default argument binding (using def test0(a: Callable = a) should be valid too but mypy doesn't like it for some reason - it is a false positive, but probably too hard to deal with or devs don't have time for it now.

Look through this issue if you are interested in mypy's opinion on that. Here's a playground to play with this online.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文