微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何为Optional [int]注释包装器?难度:当它是一个参数时

如何解决如何为Optional [int]注释包装器?难度:当它是一个参数时

我似乎找不到一个可接受的注释,该注释的接收值是:a)函数返回int或b)None。麻烦的是,该函数返回的值是父函数的可选关键字参数,因此先前已声明为Optional[int]。但是,运行时分配保证该函数永远不会返回 None

如果我删除了注释,则mypy接受它为黄金。但是我更喜欢使用一些(可接受的)注释。一分钱,一英镑...

我的代码

from typing import Optional,Callable


def myfun(p1: Optional[int] = None):
    # mypy complains about this
    dyn_p1:Optional[Callable[[],int]] = (lambda: p1) if p1 else None

    # ... but has no problem with this
    # dyn_p1 = (lambda: p1) if p1 else None

    otherFun(dyn_p1)


# I expect the parameter annotation here to be checked at the point of invocation above.
def otherFun(dyn_p1: Optional[Callable[[],int]]):
    pass

这是mypy错误

PS $ mypy .\prompt_toolkit\shortcuts\test1.py
prompt_toolkit\shortcuts\test1.py:6: error: Incompatible types in assignment (expression has type "Optional[Callable[[],Optional[int]]]",variable has type "Optional[Callable[[],int]]")
prompt_toolkit\shortcuts\test1.py:6: error: Incompatible return value type (got "Optional[int]",expected "int")
Found 2 errors in 1 file (checked 1 source file)

# comment out the first dyn_p1 and uncomment the second,run again:
PS $ mypy .\prompt_toolkit\shortcuts\test1.py
Success: no issues found in 1 source file

解决方法

只需更改以下内容

from typing import Optional,Callable


def myfun(p1: Optional[int] = None):
    dyn_p1: Optional[Callable[[],Optional[int]]] = (lambda: p1) if p1 else None
    otherFun(dyn_p1)


def otherFun(dyn_p1: Optional[Callable[[],Optional[int]]]):
    pass

这是因为您已将p1明确定义为可选int,因此,如果lambda返回p1,则dyn_p1必须能够返回可选int,而不仅仅是int。

现在运行mypy

(venv) ➜  pythonProject mypy okay.py
Success: no issues found in 1 source file
,

我(有点)不认为这里的问题是您的lambda。我认为它实际上是p1

假设您改为

def myfun(p1: int = 5):
    dyn_p1:Optional[Callable[[],int]] = (lambda: p1) if p1 > 0 else None

毕竟,lambda返回p1p1是一个整数。 lambda返回一个整数。 Lambda是Callable[[],int]。除非它为None,否则Optional使一切都变得愉快。

对于您来说,p1不是int。这是Optional[int]。因此MyPy正确地推断出它是Callable[[],Optional[int]]。那与类型提示不同,所以它发出嘶哑的声音。

现在您可能会说:“但是MyPy应该能够进行代码流分析,并且知道只有在p1不为空的情况下,lambda才会被赋值。”

哪个...是真的。 MyPy可以进行一些代码流分析。例如,可以这样:

def test(p1: Optional[int]) -> None:
    p2: Union[int,str] = p1 if p1 is not None else "Fail"

我怀疑这里的收获是 lambda绑定晚了。也就是说,尽管您在分配p1时未检查到None,但创建{{之后,仍然将其合法地设置回None 1}}。

如果您有代码

dyn_p1

即使def myfun(p1: Optional[int] = None): dyn_p1:Optional[Callable[[],int]] = (lambda: p1) if p1 else None p1 = None return dyn_p1() myfun(5) 以int开头,结果也将是p1,因为None就是执行lambda的时候。

MyPy不会分析控制流以确保此变量永远不会更改为合法类型。相反,它警告您易忘的Python规则意味着它可能不像乍看起来那样可预测。

您可以做的一件事就是引入另一个标注为p1的变量。因为将新变量更改为int是不合法的,所以MyPy可以继续担心。

None

具有讽刺意味的是,在您的示例中,如果不进行检查,MyPy不会发现后期绑定问题。它没有将其标记为def myfun(p1: Optional[int] = None): if p1 is not None: p1_: int = p1 dyn_p1:Optional[Callable[[],int]] = (lambda: p1_) else: dyn_p1 = None otherFun(dyn_p1) 或其他任何东西;它实际上可以推断出您想告诉它的类型!

Any

此类型检查正常。两次都显示为“ Union [def()-> builtins.int,None]”(这是Optional [Callable [[],int]]的MyPy拼写)。但是,如果您实际调用它,它会通过未经授权的def test(p1: Optional[int] = 5) -> int: dyn_p1 = (lambda: p1) if p1 else None reveal_type(dyn_p1) p1 = None reveal_type(dyn_p1) return dyn_p1() if dyn_p1 is not None else 0 潜行。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。