【问题标题】:How to specify type based on runtime condition如何根据运行时条件指定类型
【发布时间】:2019-12-03 21:49:16
【问题描述】:

我正在使用 mypy 和协议,如果可能的话,我遇到了一个我想要类型提示的地方,但我无法弄清楚我应该如何设置它,这样 mypy 就不会出错。

考虑以下示例:

class TProtocol(Protocol):
    t: str

@attrs(auto_attribs=True)
class T:
    t: str
    t2: str

@attrs(auto_attribs=True)
class T2:
    t: str

def func(var: TProtocol) -> None:
    if some_condition:
        var: T
        reveal_type(var)
    else:
        reveal_type(var)

虽然非常做作,但它说明了我的目标,即我有一些运行时条件,如果满足,我会根据代码库的知识知道该变量的类型。然后我想将这些知识传递给 mypy,以便进一步的类型检查使用该类型。

同样的例子可以用 Union 代替。一些运行时检查会根据代码库的知识明确告诉我我拥有哪种类型。然后,我想根据外部知识明确告诉 mypy 哪种类型用于进一步类型检查。

上面的示例引发了一个错误,指出 var 已定义。我尝试了allow_redefinition 选项,但它并没有改变输出。

【问题讨论】:

  • 这与Protocol 有多大关系?您似乎想为给定的运行时分支指定一个类型。请注意,hasattr(var, "t2") 是一个运行时表达式,没有什么可以阻止 T2 类型的值具有字段 t2
  • 你是对的,没有什么能明确阻止你将字段 t2 添加到 T2。在实际代码中,它使用枚举而不是hasattr,并且协议实际上是对象上的一个字段。基于作为对象的另一个属性的枚举,我明确地知道协议的具体类是什么。我试图使用这些知识并显式键入它,以便可以轻松地对 if 块内的代码进行类型检查。我希望通过更集中的问题示例来删除代码的详细语义。
  • 协议没有一个具体类。这才是重点。任何满足协议的类都是有效的,即使是那些你没有定义的。也许您应该更新您的问题以实际匹配您要解决的问题。
  • 你是对的,协议没有一个实现,但基于某些条件,我明确知道它可能在该代码库中的一个具体类。我基本上对类型有isinstance 风格的知识。我更新了问题以删除hasattr 以尝试更明确地从其他方式显示我知道var 必须是协议的实现,并且我想将其明确键入为该类型。现在更有意义了吗?
  • 这里有些混乱:1) 协议意味着支持满足它的所有类型。如果您打算仅使用 some 具体类型并不重要,您明确地将其注释为表示 all 匹配类型。 2) someconditionhasattr 是在实例上执行的 runtime 检查。他们没有透露任何关于类型的信息。 3)如果你想让它成为一些具体的类型,你为什么不这样注释呢? Union[T, T2] 似乎更合适。

标签: python-3.x mypy


【解决方案1】:

使用typing.cast 强制将变量声明为特定类型。这会忽略任何其他类型的信息,并且也适用于运行时分支。

def func(var: TProtocol) -> None:
    reveal_type(var)         # line 21
    if some_condition:
        var = cast(T, var)   # line 23
        reveal_type(var)
    else:
        var = cast(T2, var)  # line 26
        reveal_type(var)

这使得mypy 以不同的方式处理var 的每次转换,即在每个分支之前和内部:

type_tests.py:21: note: Revealed type is 'type_tests.TProtocol'
type_tests.py:24: note: Revealed type is 'type_tests.T'
type_tests.py:27: note: Revealed type is 'type_tests.T2'

【讨论】:

  • 我的真实代码库必须有其他事情发生,这在我的示例中按预期工作。感谢您的帮助,让我指出了正确的方向。它不像为循环定义变量那么漂亮,但它可以完成工作,这很重要。
猜你喜欢
  • 1970-01-01
  • 2023-03-14
  • 2022-10-05
  • 1970-01-01
  • 1970-01-01
  • 2019-09-02
  • 1970-01-01
  • 2019-06-01
  • 2018-02-18
相关资源
最近更新 更多