mypy "Optional[Dict[Any, Any]]" 在标准过滤器、地图中不可索引
mypy "Optional[Dict[Any, Any]]" is not indexable inside standard filter, map
给定以下代码:
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 indexable
。 map
也是如此。从 pydash 更改行以使用列表理解或 filter_
或 map_
可解决错误。
即使有类型保护,为什么 mypy 在使用标准 filter
时会抛出错误?
在 if
或 assert
之后发生的类型缩小不会传播到您绑定该变量的内部范围。简单的解决方法是定义一个新变量与较窄的类型绑定,例如:
def bar() -> None:
d = foo(False)
if not d:
return
d_exists = d
filter(lambda x: d_exists['k'], [])
d
未绑定到内部范围中较窄类型的原因可能是因为无法保证 d
不会在外部范围,例如:
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 上并在以后对其进行评估。
mypy
不模拟代码:它不知道 d
不是 None
如果实际上达到了对 filter
的调用。它只知道有人试图索引一些被静态标记为可能具有None
作为值的东西。 (换句话说,d
的静态类型不会改变,除非你实际分配一个具有不同静态类型的值。)
您可以使用 cast
功能帮助 mypy
。
from typing import Optional, Dict<b>, cast</b>
def foo(b: bool) -> Optional[Dict]:
return {} if b else None
def bar() -> None:
d = foo(False)
if not d:
return
<b>d: dict = cast(dict, d) # "Trust me, mypy: d is a dict"</b>
filter(lambda x: d['k'], [])
给定以下代码:
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 indexable
。 map
也是如此。从 pydash 更改行以使用列表理解或 filter_
或 map_
可解决错误。
即使有类型保护,为什么 mypy 在使用标准 filter
时会抛出错误?
在 if
或 assert
之后发生的类型缩小不会传播到您绑定该变量的内部范围。简单的解决方法是定义一个新变量与较窄的类型绑定,例如:
def bar() -> None:
d = foo(False)
if not d:
return
d_exists = d
filter(lambda x: d_exists['k'], [])
d
未绑定到内部范围中较窄类型的原因可能是因为无法保证 d
不会在外部范围,例如:
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 上并在以后对其进行评估。
mypy
不模拟代码:它不知道 d
不是 None
如果实际上达到了对 filter
的调用。它只知道有人试图索引一些被静态标记为可能具有None
作为值的东西。 (换句话说,d
的静态类型不会改变,除非你实际分配一个具有不同静态类型的值。)
您可以使用 cast
功能帮助 mypy
。
from typing import Optional, Dict<b>, cast</b>
def foo(b: bool) -> Optional[Dict]:
return {} if b else None
def bar() -> None:
d = foo(False)
if not d:
return
<b>d: dict = cast(dict, d) # "Trust me, mypy: d is a dict"</b>
filter(lambda x: d['k'], [])