在Python中定义具有默认值的泛型参数

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

假设我有以下代码:

from typing import Literal, TypeVar, Generic

T = TypeVar("T", bound=str)

class Foo(Generic[T]):
    def foo(self, arg: T = "foo") -> T:
        return arg

我的想法是,我有一个通用类,它有一个接受字符串的具体方法,但子类可以定义比任何字符串更严格的输入,例如这个类只接受

"bar"

class Bar(Foo[Literal["bar"]]):
    pass

但是,原始代码没有通过任何类型检查器。 mypy 和pyright 都抱怨。 mypy 错误是:

test.py:6: error: Incompatible default for argument "arg" (default has type "str", argument has type "T")

我认为这没问题,因为

T
有一个上限,它是一个字符串。但我认为问题在于,如果子类仅接受“bar”,则默认参数“foo”不再有意义。那么我如何定义
T
使其必须始终允许文字
"foo"
,但子类还可以定义它接受的其他字符串?

python python-3.x generics python-typing
2个回答
3
投票

除了评论中的建议外,还可以考虑使用

typing.overload

from typing import TYPE_CHECKING, Literal, TypeVar, Generic, overload
from typing_extensions import reveal_type

T = TypeVar("T", bound=str | Literal["foo"])


class Foo(Generic[T]):
    @overload
    def foo(self, arg: T) -> T:
        ...

    @overload
    def foo(self) -> Literal["foo"]:
        ...

    def foo(self, arg: T | Literal["foo"] = "foo") -> T | Literal["foo"]:
        return arg


class Bar(Foo[Literal["bar"]]):
    pass


if TYPE_CHECKING:
    reveal_type(Bar().foo("bar"))  # Revealed type is "Literal['bar']"
    reveal_type(Bar().foo())  # Revealed type is "Literal['foo']"

0
投票

我的想法是,我有一个带有具体方法的泛型类 接受一个字符串,但子类可以定义更严格的 输入比任何字符串

这违反了里氏替换原则(LSP),所以mypy和pyright拒绝它是正确的。子类需要接受父类接受的任何内容,否则您无法自由地用子类替换父类。但你可以做相反的事情;子类可以比父类宽容。

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