简体   繁体   English

如何在泛型类的 __init__ 中使用默认值?

[英]How to use default values in __init__ in a Generic class?

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 参数中具有默认值时,即使 init 参数定义了类型,MyPy 也会抱怨类型不匹配的可能性。

Consider the following minimal example考虑以下最小示例

class IntOrStrReplicator(Generic[IntStrType]):
    def __init__(self, a: IntStrType) -> None:
        self.a = a
    def produce(self) -> IntStrType:
        return self.a

We expect the following我们期待以下

reveal_type(IntOrStrReplicator(4)) # IntOrStrReplicator[int]
reveal_type(IntOrStrReplicator("Hello")) # IntOrStrReplicator[str]
reveal_type(IntOrStrReplicator(4).produce()) # int
reveal_type(IntOrStrReplicator("Hello").produce()) # str

Sure enough, we get that if IntStrType is either果然,如果IntStrType

TypeVar("IntStrType", bound=Union[int, str])

or或者

TypeVar("IntStrType", int, str)

Now, all I want to add is a default value of a .现在,我要补充的是一个默认值a For example def __init__(self, a: IntStrType = 4) -> None: Clearly there's no problem with creating one of these classes with a=4.例如def __init__(self, a: IntStrType = 4) -> None:明显,用 a=4 创建这些类之一没有问题。 However the line then complains.然而,线路然后抱怨。 If the TypeVar is given as a Union, it says:如果 TypeVar 作为联合给出,它会说:

Incompatible default for argument "a" (default has type "int", argument has type "T1")

and if the TypeVar is given as two different options it says如果 TypeVar 作为两个不同的选项给出,它会说

Incompatible default for argument "a" (default has type "int", argument has type "str")

As explained by user2357112, the problem with this is that __init__ only sometimes defines the type parameter, and that explicit types can fail.正如 user2357112 所解释的那样,问题在于__init__仅有时定义类型参数,而显式类型可能会失败。

To express defaults in this way, you would need to restrict the default option to the corresponding type.要以这种方式表达默认值,您需要将默认选项限制为相应的类型。 You can do this with overload with a restriction on self .您可以通过self进行限制的overload来做到这一点。 For example例如

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

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 参数中具有默认值时,即使 init 参数定义了类型,MyPy 也会抱怨类型不匹配的可能性。

The problem here is that the __init__ parameter does not define the type.这里的问题是__init__参数没有定义类型。 You can do something like你可以做类似的事情

x = IntOrStrReplicator[str]()

where the type variable is bound to str , but the default value is still 4 .其中类型变量绑定到str ,但默认值仍然是4

Your code isn't saying that IntOrStrReplicator() defaults to IntOrStrReplicator[int](4) .您的代码并不是说IntOrStrReplicator()默认为IntOrStrReplicator[int](4) Your code is saying that both IntOrStrReplicator[int]() and IntOrStrReplicator[str]() use a default argument of 4 .您的代码说IntOrStrReplicator[int]()IntOrStrReplicator[str]()使用默认参数4 IntOrStrReplicator[str](4) a type error, which is why your default value is invalid. IntOrStrReplicator[str](4)类型错误,这就是您的默认值无效的原因。

I don't think there's any way to express what you seem to have intended.我认为没有任何方法可以表达您的意图。


In the absence of an explicit type argument, type checkers will attempt to infer the type, but even then, it doesn't just go by the constructor arguments.在没有显式类型参数的情况下,类型检查器将尝试推断类型,但即便如此,它也不仅仅是通过构造函数参数。 It also takes the context into account, so in the following code:它还考虑了上下文,因此在以下代码中:

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 is deduced as int for the first Foo(3) and object for the second Foo(3) . T推导作为int用于第一Foo(3)object的第二Foo(3)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM