您可以根据传递到构造函数的参数输入提示重载方法的返回类型吗?

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

假设我有一堂这样的课:

class MyClass:
    def __init__ (self, param1: Tuple[str,...], param2: bool) -> None:
        self.member1 = param1
        self.member2 = param2
        self.member3 = 10

    def gimmie(self) -> int | Tuple[str,...]:
        return self.member1 if self.member2 else self.member3

有什么方法可以确保从 gimmie 返回的不是类型的

int | Tuple[str,...]
而是
int
Tuple[str,...]

我正在寻找一种方法来

cast
回归,而不是做一些严肃的杂技。

问题很简单,我构造了一个带有标志的对象,并且其中一个方法根据该标志返回两种类型中的一种。如果这是糟糕的设计,那么“正确”的方法是什么?

python python-3.x overloading type-hinting
2个回答
6
投票

这是使用泛型解决此问题的方法:

from __future__ import annotations
from typing import overload, Literal, Generic, TypeVar, cast

T = TypeVar('T')


class myclass(Generic[T]):
    member1: tuple[str, ...]
    member2: bool
    member3: int

    @overload
    def __init__(self: myclass[tuple[str, ...]], param1: tuple[str, ...], param2: Literal[True]) -> None:
        ...

    @overload
    def __init__(self: myclass[int], param1: tuple[str, ...], param2: Literal[False]) -> None:
        ...

    def __init__(self, param1: tuple[str, ...], param2: bool) -> None:
        self.member1 = param1
        self.member2 = param2
        self.member3 = 10

    def gimmie(self) -> T:
        return cast(T, self.member1 if self.member2 else self.member3)


reveal_type(myclass(('a', 'b'), True).gimmie())
# note: Revealed type is "builtins.tuple*[builtins.str]"

reveal_type(myclass(('a', 'b'), False).gimmie())
# note: Revealed type is "builtins.int*"

一些注意事项:

  • 此方法需要注释
    self
    参数以赋予其不同的静态类型。通常,我们不会注释
    self
    ,所以请确保不要忘记这一点!
  • 遗憾的是,如果不添加
    a if b else c
    ,我就无法让
    cast
    拥有正确的类型。

我确实同意 Samwise 的观点,这种类型的柔道是一种代码味道,并且可能隐藏了项目设计的问题。


2
投票

这是使用子类和

@overload
ed 工厂函数来解决这个问题的一种方法:

from typing import Literal, Tuple, Union, cast, overload


class MyClass:
    def __init__(self, param1: Tuple[str, ...], param2: bool) -> None:
        self.member1 = param1
        self.__member2 = param2
        self.member3 = 10

    def gimmie(self) -> Union[int, Tuple[str, ...]]:
        return self.member1 if self.__member2 else self.member3


class _MySubclass1(MyClass):
    def gimmie(self) -> Tuple[str, ...]:
        return cast(Tuple[str, ...], MyClass.gimmie(self))


class _MySubclass2(MyClass):
    def gimmie(self) -> int:
        return cast(int, MyClass.gimmie(self))


@overload
def myclass(param1: Tuple[str, ...], param2: Literal[True]) -> _MySubclass1:
    ...


@overload
def myclass(param1: Tuple[str, ...], param2: Literal[False]) -> _MySubclass2:
    ...


def myclass(param1: Tuple[str, ...], param2: bool) -> MyClass:
    if param2:
        return _MySubclass1(param1, param2)
    else:
        return _MySubclass2(param1, param2)


myobj1 = myclass((), True)
myobj2 = myclass((), False)
reveal_type(myobj1.gimmie())  # Revealed type is "builtins.tuple[builtins.str]"
reveal_type(myobj2.gimmie())  # Revealed type is "builtins.int"

请注意,这是一项繁重的工作,需要仔细注意以确保

cast
与实现逻辑相匹配——我不知道您要解决的现实问题,但必须经历这个使输入正确排列的麻烦通常是数据建模方式中的“味道”。

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