【问题标题】:Python dynamic type hintingPython动态类型提示
【发布时间】:2018-08-07 16:28:13
【问题描述】:

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

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)

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

【问题讨论】:

  • PEP 484:非目标 虽然建议的类型模块将包含一些用于运行时类型检查的构建块——特别是 get_type_hints() 函数——但必须开发第三方包来实现特定的运行时类型检查功能,例如使用装饰器或元类。使用类型提示进行性能优化留给读者作为练习。
  • @BoarGules 我不相信你明白我在说什么。我正在寻找基于提供给函数的常量值的静态推断。请注意,我在示例中给出了这个 - 一个足够先进的静态检查器必须能够理解提供给函数的值是一个常量,因此可以在不实际运行代码的情况下对其进行检查。
  • @AndreyCizov 你找到答案了吗,我也在找类似的东西。
  • 目前没有办法做到这一点@moshevi
  • 由于您现在拒绝了两个似乎满足问题的答案,并引用了问题中陈述的要求not,请更新问题以澄清您在寻找什么。如果您想要“mypy 的插件,而不是默认 python 设置中所述功能的可行性”,那么问题应该明确说明。

标签: python-3.x dynamic-typing


【解决方案1】:

没有通用的方法可以做到这一点:就静态类型检查而言,诸如*types 之类的可变参数不会保持顺序。 Variadic generics cover this behaviour but are only proposals so far.

可以对可变参数使用单个注释,这会降级为通用基本类型,也可以使用多个注释来枚举最常见的情况。


将可变参数注释为类型变量(可能通过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*]

这在使用类似类型(例如仅AA 的子类)并且仅期望一般功能(例如仅A)时就足够了。这在使用混合类型(例如AB)时是不够的,因为它会退化为通用的基本类型(例如Base)。


为合理数量的参数提供multiple @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*]'

这里的限制只是一个人认为相关的案例数量。 This mechanism is also used for the standard library.

【讨论】:

  • 问题是关于动态类型提示以及是否可以添加这个,比如 mypy 的插件,而不是默认 python 设置中上述功能的可行性
  • @AndreyCizov 然后问题应该这样说明。目前,它说“我应该如何注释事务,以便类型检查器可以正确推断从它返回的值的类型?”这正是这个答案所提供的。
  • 你没有提供通用的解决方案
  • @AndreyCizov 好吧,在断言不存在这样的解决方案之后,这将是一件奇怪的事情。我是否应该将其保留在该断言中并删除适用于任意多个但不是无限多个参数的解决方案?
  • @AndreyCizov 添加了多年来一直停滞不前的开放 mypy 提案的参考。方便地包括几乎完全将类型序列转换为实例序列的用例。
猜你喜欢
  • 2021-12-25
  • 1970-01-01
  • 2016-04-15
  • 2016-12-23
  • 2020-11-30
  • 2022-09-29
  • 2016-03-17
  • 2021-11-25
  • 2020-01-14
相关资源
最近更新 更多