【问题标题】:mypy "Optional[Dict[Any, Any]]" is not indexable inside standard filter, mapmypy "Optional[Dict[Any, Any]]" 在标准过滤器、地图中不可索引
【发布时间】:2020-08-05 17:15:10
【问题描述】:

给定以下代码:

from typing import Optional, Dict

def foo(b: bool) -> Optional[Dict]:
    return {} if b else None


def bar() -> None:
    d = foo(False)

    if not d:
        return

    filter(lambda x: d['k'], [])

mypy 0.770 在bar 的最后一行出现以下错误:Value of type "Optional[Dict[Any, Any]]" is not indexablemap 也是如此。从 pydash 更改行以使用列表理解或 filter_map_ 可解决错误。

为什么 mypy 在使用标准 filter 时会抛出错误,即使有类型保护?

【问题讨论】:

    标签: python python-3.x type-hinting mypy


    【解决方案1】:

    ifassert 之后发生的类型缩小不会传播到您绑定该变量的内部范围。简单的解决方法是定义一个与更窄类型绑定的新变量,例如:

    def bar() -> None:
        d = foo(False)
    
        if not d:
            return
        d_exists = d
    
        filter(lambda x: d_exists['k'], [])
    

    d 未绑定到内部范围内的较窄类型的原因可能是因为无法保证 d 可能不会在外部范围内更改回 None,例如:

    def bar() -> None:
        d = foo(False)
    
        if not d:
            return
    
        def f(x: str) -> str:
            assert d is not None  # this is totally safe, right?
            return d['k']         # mypy passes because of the assert
    
        d = None  # oh no!
        filter(f, [])
    

    而如果您绑定一个新变量,则该赋值是不可能的:

    def bar() -> None:
        d = foo(False)
    
        if not d:
            return
        d_exists = d
    
        def f(x: str) -> str:
            # no assert needed because d_exists is not Optional
            return d_exists['k']
    
        d_exists = None  # error: Incompatible types in assignment
        filter(f, [])
    

    在您的特定示例中,没有运行时危险,因为 lambda 立即由 filter 评估,同时您没有机会更改 d,但 mypy 不一定有一种简单的方法来确定你调用的函数不会挂在那个 lambda 上并在以后评估它。

    【讨论】:

    • 这是否意味着filter_ 可以工作,因为mypy 知道在其他任何东西有机会修改d 之前对lambda 进行评估,而filter 不起作用,因为它构造了一个迭代器和mypy 没有简单的方法来了解该迭代器的工作原理?
    • 我不肯定,因为我根本不熟悉 pydash,但我怀疑 filter_“有效”,因为它没有类型(或类型不好),所以表达式根本没有类型检查.通过使用无类型接口来简单地禁用类型检查并不难。我必须实际看到它的一个例子才能得到更好的答案。
    • 我明白了。我认为直到稍后才会调用 lambda 的可能性是答案,然后,也解释了为什么列表理解有效。
    • 是的,通过列表理解,您不会绑定一个会在不确定的时间内徘徊的 lambda。如果你有一个类似的过滤功能(比如你通过(d.get, 'k')而不是lambda x: d['k']),那么这也可以避免这个问题。
    【解决方案2】:

    mypy 不模拟代码:它不知道d 不是None,如果实际到达了对filter 的调用。它只知道试图索引被静态标记为可能具有None 作为值的东西。 (换句话说,d 的静态类型不会改变,除非您实际分配一个具有不同静态类型的值。)

    您可以使用cast 函数帮助mypy

    from typing import Optional, Dict, cast
    
    def foo(b: bool) -> Optional[Dict]:
        return {} if b else None
    
    
    def bar() -> None:
        d = foo(False)
    
        if not d:
            return
    
        d: dict = cast(dict, d)  # "Trust me, mypy: d is a dict"
    
        filter(lambda x: d['k'], [])

    【讨论】:

    • 我可能仍然误解了一些东西,但我认为这并不能解释为什么使用列表理解或filter_ 可以在没有给 mypy 额外提示的情况下工作。
    猜你喜欢
    • 1970-01-01
    • 2021-06-14
    • 2021-12-31
    • 2022-01-07
    • 2020-08-29
    • 2020-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多