如何注释将 AnyStr 设为 str 默认值的函数?

How do I annotate a function that takes AnyStr with a str default value?

我希望对一个函数进行类型注释,该函数采用默认为 strAnyStr 参数以及相同类型的 returns 和 AnyStr。但是,如果我这样写:

from typing import AnyStr

def func(s: AnyStr = ".") -> AnyStr:
    return s

然后 mypy 失败并显示“Incompatible default for argument "s" (default has type "str", argument has type "bytes")”。

我还尝试将代码拆分为 .py.pyi 文件,如下所示:

.py 文件:

def func(s = "."):
    return s

.pyi 文件:

from typing import AnyStr

def func(s: AnyStr = ...) -> AnyStr:
    ...

...但我一定是错误地调用了 mypy,因为它无法对 func 的调用进行类型检查;例如,如果我将 func(42) 添加到 .py 文件,mypy 不会抱怨。

注释我的函数并让代码进行完全类型检查的正确方法是什么?

使用 None 作为默认值,然后在函数内部使用两个转换:一个将 None 替换为“.”转换为 Optional[AnyStr],将 return 值转换为 AnyStr.

def func(s : AnyStr = None) -> AnyStr:
    if s is None:
        s = cast(Optional[AnyStr], ".")
    return cast(AnyStr, s)

编辑:我原来的回答失败了,因为类型检查忽略了具体实现:

一种可能是用overload枚举AnyStr涵盖的两种情况:

from typing import overload


@overload
def func(s: str = ".") -> str:
    pass


@overload
def func(s: bytes) -> bytes:
    pass


def func(s):
    return s 

讨论了此案例 here。特别是 Guido 的回答:

I think the problem here is that in general, e.g. if there are other parameters also using _T in their type, the default value won't work. Since this is the only parameter, you could say that this is overly restrictive, and if there are no parameters, the return type should just be determined by that default value (in your simplified example, you'd want it to return int). But I'm not so keen on doing that, since it breaks as soon as another generic parameter is used.

基于该线程,您可以使用以下解决方法:

from typing import AnyStr, overload


@overload
def func() -> str: ...

@overload
def func(s: AnyStr) -> AnyStr: ...

def func(s = "."):  # unchecked implementation
    return func_internal(s)

def func_internal(s: AnyStr) -> AnyStr:
    # a checked complicated body moved here
    return s

也许这就是你想要的:

@overload
def func(s: str=...) -> str: ...   
@overload
def func(s: bytes) -> bytes: ...
@overload
def func(s: None) -> str: ...    

def func(s="."):
    return s


r1 = func()
reveal_type(r1)
r2 = func("1")
reveal_type(r2)
r3 = func(b"1")
reveal_type(r3)

Mypy: The stdout of the command line is:
note: Revealed type is 'builtins.str'
note: Revealed type is 'builtins.str'
note: Revealed type is 'builtins.bytes'