重载以下可选参数

问题描述 投票:0回答:4

我有一个类

Animal
,其方法
foo
根据布尔参数
inplace
具有不同的返回类型,该布尔参数跟随可选参数
bar
。我想重载该函数,以便在已知
inplace
的值时知道返回类型

这是我的代码:

# main.py

from __future__ import annotations

from typing import Optional, overload, Literal 


class Animal:
    @overload
    def foo(self, bar=..., inplace: Literal[False]=...) -> Animal:
        ...

    @overload
    def foo(self, bar=..., inplace: Literal[True]=...) -> None:
        ...

    def foo(
        self, bar=None, inplace: bool = False
    ) -> Optional[Animal]:
        ...


reveal_type(Animal().foo(bar='a'))
reveal_type(Animal().foo(inplace=True))
reveal_type(Animal().foo(inplace=False))
$ mypy main.py
main.py:8: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
main.py:21: note: Revealed type is 'main.Animal'
main.py:22: note: Revealed type is 'None'
main.py:23: note: Revealed type is 'main.Animal'
Found 1 error in 1 file (checked 1 source file)

https://mypy-play.net/?mypy=latest&python=3.9&gist=49da369f6343543769eed2060fa61639

如何避免第 8 行出现

Overloaded function signatures 1 and 2 overlap with incompatible return types
错误?

python mypy static-typing
4个回答
3
投票

这似乎有效:

from __future__ import annotations

from typing import Optional, overload, Literal 


class Animal:

    # using defaults
    @overload
    def foo(self, bar=..., inplace: Literal[False]=...) -> Animal: ...

    # using inplace = True
    
    # with bar
    @overload
    def foo(self, bar, inplace: Literal[True]) -> None: ...

    # without bar
    @overload
    def foo(self, *, inplace: Literal[True]) -> None: ...

    # with bool
    @overload
    def foo(self, bar=..., inplace: bool=...) -> Optional[Animal]: ...

    def foo(
        self, bar=None, inplace = False
    ):
        ...


reveal_type(Animal().foo(bar='a'))
reveal_type(Animal().foo(bar='a', inplace=True))
reveal_type(Animal().foo(bar='a', inplace=False))
reveal_type(Animal().foo(inplace=True))
reveal_type(Animal().foo(inplace=False))
reveal_type(Animal().foo())

inplace: bool
reveal_type(Animal().foo(bar='a', inplace=inplace))
reveal_type(Animal().foo(inplace=inplace))

很多超载,但也许这是不可避免的


1
投票

Mypy 上的以下问题密切相关:https://github.com/python/mypy/issues/5486

# main.py

from __future__ import annotations

from typing import Literal, Optional, overload


class Animal:
    @overload
    def foo(self, bar=..., inplace: Literal[False] = ...) -> Animal:
        ...

    @overload
    def foo(self, bar=..., *, inplace: Literal[True]) -> None:
        ...

    def foo(self, bar=None, inplace: bool = False) -> Optional[Animal]:
        ...


reveal_type(Animal().foo(bar="a"))
reveal_type(Animal().foo(inplace=True))
reveal_type(Animal().foo(inplace=False))
$ mypy main.py 
main.py:21: note: Revealed type is "main.Animal"
main.py:22: note: Revealed type is "None"
main.py:23: note: Revealed type is "main.Animal"

通过在第二个重载的

*,
参数之前添加
inplace
,并删除其默认值,它确实可以按预期工作。这是在此评论上建议的。

但是,如果您尝试使用位置参数调用该函数,这确实会引入一个问题:

reveal_type(Animal().foo("a", True))
main.py:25: error: No overload variant of "foo" of "Animal" matches argument types "str", "bool"
main.py:25: note: Possible overload variants:
main.py:25: note:     def foo(self, bar: Any = ..., inplace: Literal[False] = ...) -> Animal
main.py:25: note:     def foo(self, bar: Any = ..., *, inplace: Literal[True]) -> None
main.py:25: note: Revealed type is "Any"
Found 1 error in 1 file (checked 1 source file)

另一个评论中提到了这个问题,建议添加另一个重载可以修复它。


0
投票

尝试:

@overload
def foo(self, inplace: Literal[False]=..., bar=...) -> Animal:
    ...

@overload
def foo(self, inplace: Literal[True], bar=...,) -> None:
    ...

def foo(self, inplace=False, bar=None):
    ...

我更改了参数的顺序,否则第二个重载应该不正确。


0
投票

假设你有一个函数:

def fct(a: str = "", b: str = "", c: str = "", flag_len: bool = False, d: str = "", e: str = ""):
    """Return list of parameters a, b, c, d, e which are not empty strings"""
    lst_non_empty = list(filter(lambda x: len(x) > 0, [a, b, c, d , e]))
    if flag_len:
        return len(lst_non_empty)
    return lst_non_empty

回归

fct(..., flag_len=True, ...) -> int
fct(..., flag_len=False, ...) -> List[str]

以下完整代码示例使用

typing.overload
将其编码到 mypy.txt 中。只需运行
$ mypy demo.py
来测试您的重载定义是否正确。这是通过
assert_type
实现的。

demo.py

from typing import List, Literal, overload
from typing_extensions import assert_type

@overload
def fct(a: str = ..., b: str = ..., c: str = ..., flag_len: Literal[False] = ..., d: str = ..., e: str = ...) -> List[str]:
    ...

@overload
def fct(*, a: str = ..., b: str = ..., c: str = ..., flag_len: Literal[True], d: str = ..., e: str = ...) -> int:
    ...

@overload
def fct(a: str, *, b: str = ..., c: str = ..., flag_len: Literal[True], d: str = ..., e: str = ...) -> int:
    ...

@overload
def fct(a: str, b: str, *, c: str = ..., flag_len: Literal[True], d: str = ..., e: str = ...) -> int:
    ...

@overload
def fct(a: str, b: str, c: str, flag_len: Literal[True], d: str = ..., e: str = ...) -> int:
    ...

def fct(a: str = "", b: str = "", c: str = "", flag_len: bool = False, d: str = "", e: str = ""):
    """
    Return list of parameters a, b, c, d, e which are not empty strings.
    flag_len returns just the length of the created list. This argument is in the middle, for demonstrating purposes.
    I would recommend it to be the first default argument, as this reduces the effort for overload.
    """
    lst_non_empty = list(filter(lambda x: len(x) > 0, [a, b, c, d , e]))
    if flag_len:
        return len(lst_non_empty)
    return lst_non_empty



def test_fct():
    assert fct("a", "b", "c") == ["a", "b", "c"]
    assert fct("a", "b", "c", True) == 3
    assert fct("a", "b", "c", e="e", d="d") == ["a", "b", "c", "d", "e"]
    assert fct("a", "b", "c", True, e="e", d="d") == 5

# CHECK RETURN TYPE OF FCT VIA MYPY
# $ mypy filename.py

assert_type(fct(), List[str])
assert_type(fct("a"), List[str])
assert_type(fct("a", "b"), List[str])
assert_type(fct("a", "b", "c"), List[str])
assert_type(fct("a", "b", "c", d="d"), List[str])
assert_type(fct("a", "b", "c", e="d"), List[str])
assert_type(fct("a", "b", "c", e="d", d="d"), List[str])
assert_type(fct("a", "b", e="d", d="d"), List[str])
assert_type(fct("a", e="d", d="d"), List[str])

assert_type(fct(flag_len=False), List[str])
assert_type(fct("a", flag_len=False), List[str])
assert_type(fct("a", "b", flag_len=False), List[str])
assert_type(fct("a", "b", "c", False), List[str])
assert_type(fct("a", "b", "c", flag_len=False), List[str])
assert_type(fct("a", "b", "c", False, d="d"), List[str])
assert_type(fct("a", "b", "c", flag_len=False, d="d"), List[str])
assert_type(fct("a", "b", "c", e="d", flag_len=False), List[str])
assert_type(fct("a", "b", "c", e="d", d="d", flag_len=False), List[str])
assert_type(fct("a", "b", flag_len=False, e="d", d="d"), List[str])
assert_type(fct("a", e="d", flag_len=False, d="d"), List[str])

assert_type(fct(flag_len=True), int)
assert_type(fct("a", flag_len=True), int)
assert_type(fct("a", "b", flag_len=True), int)
assert_type(fct("a", "b", "c", True), int)
assert_type(fct("a", "b", "c", flag_len=True), int)
assert_type(fct("a", "b", "c", True, d="d"), int)
assert_type(fct("a", "b", "c", flag_len=True, d="d"), int)
assert_type(fct("a", "b", "c", e="d", flag_len=True), int)
assert_type(fct("a", "b", "c", e="d", d="d", flag_len=True), int)
assert_type(fct("a", "b", flag_len=True, e="d", d="d"), int)
assert_type(fct("a", e="d", flag_len=True, d="d"), int)

重载中的

...
告诉 mypy 该参数可能存在或不存在。 重载中的
*
告诉 mypy 后面的所有内容都是关键字参数。

© www.soinside.com 2019 - 2024. All rights reserved.