如何在通用 class 中使用 __init__ 中的默认值?
How to use default values in __init__ in a Generic class?
当泛型 class 在 init 参数中有默认值时,MyPy 会抱怨类型不匹配的可能性,即使 init 参数定义了类型。
考虑以下最小示例
class IntOrStrReplicator(Generic[IntStrType]):
def __init__(self, a: IntStrType) -> None:
self.a = a
def produce(self) -> IntStrType:
return self.a
我们期待以下内容
reveal_type(IntOrStrReplicator(4)) # IntOrStrReplicator[int]
reveal_type(IntOrStrReplicator("Hello")) # IntOrStrReplicator[str]
reveal_type(IntOrStrReplicator(4).produce()) # int
reveal_type(IntOrStrReplicator("Hello").produce()) # str
果然,如果 IntStrType
是
TypeVar("IntStrType", bound=Union[int, str])
或
TypeVar("IntStrType", int, str)
现在,我只想添加一个默认值 a
。例如 def __init__(self, a: IntStrType = 4) -> None:
显然,使用 a=4 创建其中一个 class 没有问题。然而,该线路然后抱怨。如果 TypeVar 作为联合给出,它表示:
Incompatible default for argument "a" (default has type "int", argument has type "T1")
如果 TypeVar 作为两个不同的选项给出,它会说
Incompatible default for argument "a" (default has type "int", argument has type "str")
When a Generic class has a default value in an init parameter, MyPy complains about the possibility of a type mismatch even though the init parameter defines the type.
这里的问题是__init__
参数没有定义类型。你可以做类似
的事情
x = IntOrStrReplicator[str]()
其中类型变量绑定到str
,但默认值仍然是4
。
您的代码并不是说 IntOrStrReplicator()
默认为 IntOrStrReplicator[int](4)
。您的代码表示 IntOrStrReplicator[int]()
和 IntOrStrReplicator[str]()
都使用默认参数 4
。 IntOrStrReplicator[str](4)
类型错误,这就是您的默认值无效的原因。
我认为没有任何方式可以表达您的意图。
在没有显式类型参数的情况下,类型检查器将尝试推断 类型,但即便如此,它也不只是通过构造函数参数。它还考虑了上下文,因此在以下代码中:
from typing import Generic, TypeVar
T = TypeVar('T')
class Foo(Generic[T]):
def __init__(self, x: T):
self.x = x
def f(x: Foo[int]): pass
def g(x: Foo[object]): pass
f(Foo(3))
g(Foo(3))
T
第一个 Foo(3)
推导为 int
,第二个 Foo(3)
推导为 object
。
正如 user2357112 所解释的,问题是 __init__
有时只定义类型参数,显式类型可能会失败。
要以这种方式表示默认值,您需要将默认选项限制为相应的类型。您可以使用 overload
和 a restriction on self
来完成此操作。例如
class IntOrStrReplicator(Generic[T1]):
@overload
def __init__(self: "IntOrStrReplicator[int]") -> None:
# int variant, only, allows leaving the parameter implied
...
@overload
def __init__(self: "IntOrStrReplicator[T1]", a:T1) -> None:
# supplying the parameter requires that the type match.
...
def __init__(self, a = 4) -> None:
# This function needs programmer care because `a` still isn't checked
# but consumers of the class take the overload definitions.
self.a: T1 = a
当泛型 class 在 init 参数中有默认值时,MyPy 会抱怨类型不匹配的可能性,即使 init 参数定义了类型。
考虑以下最小示例
class IntOrStrReplicator(Generic[IntStrType]):
def __init__(self, a: IntStrType) -> None:
self.a = a
def produce(self) -> IntStrType:
return self.a
我们期待以下内容
reveal_type(IntOrStrReplicator(4)) # IntOrStrReplicator[int]
reveal_type(IntOrStrReplicator("Hello")) # IntOrStrReplicator[str]
reveal_type(IntOrStrReplicator(4).produce()) # int
reveal_type(IntOrStrReplicator("Hello").produce()) # str
果然,如果 IntStrType
是
TypeVar("IntStrType", bound=Union[int, str])
或
TypeVar("IntStrType", int, str)
现在,我只想添加一个默认值 a
。例如 def __init__(self, a: IntStrType = 4) -> None:
显然,使用 a=4 创建其中一个 class 没有问题。然而,该线路然后抱怨。如果 TypeVar 作为联合给出,它表示:
Incompatible default for argument "a" (default has type "int", argument has type "T1")
如果 TypeVar 作为两个不同的选项给出,它会说
Incompatible default for argument "a" (default has type "int", argument has type "str")
When a Generic class has a default value in an init parameter, MyPy complains about the possibility of a type mismatch even though the init parameter defines the type.
这里的问题是__init__
参数没有定义类型。你可以做类似
x = IntOrStrReplicator[str]()
其中类型变量绑定到str
,但默认值仍然是4
。
您的代码并不是说 IntOrStrReplicator()
默认为 IntOrStrReplicator[int](4)
。您的代码表示 IntOrStrReplicator[int]()
和 IntOrStrReplicator[str]()
都使用默认参数 4
。 IntOrStrReplicator[str](4)
类型错误,这就是您的默认值无效的原因。
我认为没有任何方式可以表达您的意图。
在没有显式类型参数的情况下,类型检查器将尝试推断 类型,但即便如此,它也不只是通过构造函数参数。它还考虑了上下文,因此在以下代码中:
from typing import Generic, TypeVar
T = TypeVar('T')
class Foo(Generic[T]):
def __init__(self, x: T):
self.x = x
def f(x: Foo[int]): pass
def g(x: Foo[object]): pass
f(Foo(3))
g(Foo(3))
T
第一个 Foo(3)
推导为 int
,第二个 Foo(3)
推导为 object
。
正如 user2357112 所解释的,问题是 __init__
有时只定义类型参数,显式类型可能会失败。
要以这种方式表示默认值,您需要将默认选项限制为相应的类型。您可以使用 overload
和 a restriction on self
来完成此操作。例如
class IntOrStrReplicator(Generic[T1]):
@overload
def __init__(self: "IntOrStrReplicator[int]") -> None:
# int variant, only, allows leaving the parameter implied
...
@overload
def __init__(self: "IntOrStrReplicator[T1]", a:T1) -> None:
# supplying the parameter requires that the type match.
...
def __init__(self, a = 4) -> None:
# This function needs programmer care because `a` still isn't checked
# but consumers of the class take the overload definitions.
self.a: T1 = a