假设我有以下代码:
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"
,但子类还可以定义它接受的其他字符串?
除了评论中的建议外,还可以考虑使用
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']"
我的想法是,我有一个带有具体方法的泛型类 接受一个字符串,但子类可以定义更严格的 输入比任何字符串
这违反了里氏替换原则(LSP),所以mypy和pyright拒绝它是正确的。子类需要接受父类接受的任何内容,否则您无法自由地用子类替换父类。但你可以做相反的事情;子类可以比父类更宽容。