无法猜测为什么重载函数实现不接受所有可能的参数

Cannot guess why Overloaded function implementation does not accept all possible arguments

我想完整地输入我的 Python 项目。但是我坚持使用可以使用不同参数调用的构造函数。

我尝试从最终构造函数中删除类型,我尝试删除一些构造函数...但仍然遇到同样的问题。

class PageObject(ABC):
    logger = logging.getLogger(__name__)

    @overload
    def __init__(self, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, by: Tuple[By, str], driver: Driver) -> None:
        ...

    @overload
    def __init__(self, context: WebElement, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, by: Tuple[By, str], parent: "PageObject") -> None:
        ...

    @overload
    def __init__(self, parent: "PageObject") -> None:
        ...

    def __init__(
        self,
        by: Optional[Tuple[By, str]] = None,
        context: Optional[WebElement] = None,
        parent: Optional["PageObject"] = None,
        driver: Optional[Driver] = None,
    ) -> None:

        if by and context:
            raise ValueError("You cannot provide a locator AND a context.")
        # ...

当我 运行 mypy 我得到以下错误:

base/page_object.py:36: 错误:重载函数实现不接受签名 1

的所有可能参数

base/page_object.py:36: 错误:重载函数实现不接受签名 2

的所有可能参数

base/page_object.py:36: 错误:重载函数实现不接受签名 3

的所有可能参数

base/page_object.py:36: 错误:重载函数实现不接受签名 4

的所有可能参数

base/page_object.py:36: 错误:重载函数实现不接受签名 5

的所有可能参数

问题来了。假设有人尝试 运行 PageObject(Driver()) —— 也就是说,我们传入一个 Driver 对象作为第一个参数。

这与您的第一次重载匹配,因此将由 mypy 进行类型检查。但是在运行时实际上发生了什么?第一个 runtime 参数是 by,所以你的 Driver 对象被分配给 bynot driver。所以现在你的类型不匹配,因为 by 应该是 Optional[Tuple[By, str]].

类型

可能最简单的解决方法是禁止您的用户完全使用位置参数 mandate that they use only keyword arguments。您可以这样做:

class PageObject:
    @overload
    def __init__(self, *, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, *, by: Tuple[By, str], driver: Driver) -> None:
        ...

    @overload
    def __init__(self, *, context: WebElement, driver: Driver) -> None:
        ...

    @overload
    def __init__(self, *, by: Tuple[By, str], parent: "PageObject") -> None:
        ...

    @overload
    def __init__(self, *, parent: "PageObject") -> None:
        ...

    def __init__(
        self,
        *,
        by: Optional[Tuple[By, str]] = None,
        context: Optional[WebElement] = None,
        parent: Optional["PageObject"] = None,
        driver: Optional[Driver] = None,
    ) -> None:
        ...

现在,mypy 对此进行了类型检查而没有错误,并且执行 PageObject(Driver()) 被 mypy 和 Python 都视为错误。相反,您现在需要执行 PageObject(driver=Driver()).

如果您确实想要允许位置参数,恐怕您需要重新设计代码。也许您可以考虑使用静态方法或类方法等,这样您就可以拥有不同的 "flavors" 构造函数——基本上,就是评论中建议的工厂模式。

可能与某人相关:

这按预期工作,请参阅我的示例:

from typing import  Any, Optional, overload, Union

@overload
def a(b: str, c: None) -> int:
    ...

@overload
def a(b: int, c: int) -> str:
    ...

def a(b: Any, c: Any) -> Any:
    if isinstance(b, str):
        return int(b)
    if isinstance(b, int):
        return str(b * c)

lalala = a('test', None)  # ok
lala = a(2, 1)  # ok
la = a('test', 'cooltest')  # an error
l = a(True, False)  # not an error ? I guess mypy treats booleans as ints here
m = a(bytes(123), bytes(123))  # an error

Guido 在此处对 msg379769 的回答 https://bugs.python.org/issue42169