Python 动态类型提示

And*_*zov 11 dynamic-typing python-3.x

我希望能够提供一个带有类型元组的函数,然后将其用于反序列化从事务中返回的数据。如果事务成功,该函数将返回这些类型的实例。例如:

T = TypeVar('T')

class Base:
    @classmethod
    def deserialize(cls: Type[T], bts: bytes) -> T:
        return cls(**json.loads(bts))

    @classmethod
    def transaction(cls, *types):
        items = self.db.transact([t.generate_load_operation() for t in types])
        items = [t.deserialize(item) for item in items]

        # how do I type-hint transaction, so that it would imply that
        # it will always return a tuple (or a list) of instances of classes 
        # contained in variable types?
        return items

class A(Base):
    pass

class B(Base):
    pass

a_inst, b_inst = Base.transaction(A, B)
Run Code Online (Sandbox Code Playgroud)

我应该如何注释事务,以便类型检查器可以正确推断从它返回的值的类型?

Mis*_*agi 4

没有通用的方法可以做到这一点:*types就静态类型检查而言,诸如此类的可变参数不保留顺序。可变参数泛型涵盖了这种行为,但到目前为止只是建议

人们可以对可变参数使用单个注释,这会降级为公共基本类型,或者使用需要枚举最常见情况的多个注释。


将可变参数注释为类型变量(可能通过 绑定Base)。这会将所有类型推断为相同的、最常见的基本类型。

class Base:
    @classmethod
    def transaction(cls, *types: Type[T]) -> List[T]: ...

class A(Base): ...

class B(Base): ...

reveal_type(Base.transaction(A, A))  # builtins.list[mt.A*]
reveal_type(Base.transaction(A, B))  # builtins.list[mt.Base*]
Run Code Online (Sandbox Code Playgroud)

A当使用类似类型(例如 just或As 的子类)并且仅期望一般功能(例如 just A's)时,这就足够了。A当使用混合类型(例如和)时,这还不够,B因为它将退化为公共基本类型(例如Base)。


为合理数量的参数提供多个@overload签名,并为大量参数使用可变参数包罗万象。这将为指定情况推断正确的类型,否则使用最常见的基本类型。


class Base:
    # explicitly enumerated types
    @overload
    @classmethod
    def transaction(cls, t1: Type[T1], /) -> Tuple[T1]: ...
    @overload
    @classmethod
    def transaction(cls, t1: Type[T1], t2: Type[T2], /) -> Tuple[T1, T2]: ...
    # catch all number of types
    @overload
    @classmethod
    def transaction(cls, *ts: Type[T]) -> Tuple[T, ...]: ...

    # implementation
    @classmethod
    def transaction(cls, *types: Type[T]) -> Tuple[T, ...]: ...

class A(Base): ...

class B(Base): ...

reveal_type(Base.transaction(A))        # Revealed type is 'Tuple[mt.A*]'
reveal_type(Base.transaction(A, A))     # Revealed type is 'Tuple[mt.A*, mt.A*]'
reveal_type(Base.transaction(A, B))     # Revealed type is 'Tuple[mt.A*, mt.B*]'
reveal_type(Base.transaction(A, B, A))  # Revealed type is 'builtins.tuple[mt.Base*]'
Run Code Online (Sandbox Code Playgroud)

这里的限制只是人们认为相关的案例数量。标准库也使用这种机制。

  • @AndreyCizov那么问题应该这样表述。目前,它说“我应该如何**注释事务**以便类型检查器可以正确推断从它返回的值的类型?” 这正是这个答案所提供的。 (2认同)
  • @AndreyCizov好吧,在断言不存在这样的解决方案之后,这将是一件奇怪的事情。我是否应该将其保留在该断言上,并删除适用于任意多个但不适用于无限多个参数的解决方案? (2认同)