【问题标题】:Python typing: Narrowing the scope of output subclass instance by base class parameterPython类型:通过基类参数缩小输出子类实例的范围
【发布时间】:2022-02-17 07:14:16
【问题描述】:

如果没有例子,我发现这很难描述。

from typing import List, Type, Optional, cast

class Base():
    kind = 'base'
class Child1(Base):
    kind = 'child1'
class Child2(Base):
    kind = 'child2'
class Child3(Base):
    kind = 'child3'

def find_in_list(lst: List[Base], SearchClass: Type[Base]): # Return Optional[??]
    for obj in lst: # type: Base
        if obj.kind == SearchClass.kind:
          return obj
    return None

lst = [Child1(), Child2(), Child3()]
def func2(c: Child2) -> None:
    assert isinstance(c, Child2)
res2_opt: Optional[Child2] = find_in_list(lst, Child2)
if res2_opt:
    func2(res2_opt)


def func3(c: Child3) -> None:
    assert isinstance(c, Child3)
res3_opt: Optional[Child3] = find_in_list(lst, Child2) # Should be Error!
if res3_opt:
    func3(res3_opt) # Is AssertionError

因为find_in_list 的返回类型太开放,mypy 不认为这有什么问题,但它在运行时正确地命中了断言错误。我试过-> Optional[SearchClass],但它(正确地)没有将其识别为一种类型。

应该可以缩小find_in_list的返回类型的范围,以与SearchClass相同或相同的方式被参数化,这样如果你传入Child2作为参数,你可以将输出限制为Optional[Child2]。如何做到这一点?

【问题讨论】:

标签: python mypy python-typing


【解决方案1】:

您应该使用TypeVar 的通用函数并转换返回值:

from typing import TypeVar, cast
T = TypeVar('T', bound=Base)
def find_in_list(lst: List[Base], SearchClass: Type[T]) -> Optional[T]:
    for obj in lst:
        if obj.kind == SearchClass.kind:
            return cast(T, obj)
    return None

然后:

res3_opt: Optional[Child3] = find_in_list(lst, Child2)
# Mypy: Incompatible types in assignment (expression has type "Optional[Child2]", 
# variable has type "Optional [Child3]")

【讨论】:

  • 谢谢!主要依靠打字模块文档和stackoverflow,这对我来说并不明显。查看您的答案,并意识到将类型 T(在输入参数化和输出参数化之间相同)限制为其他输入参数化的通用绑定 Base 的关键是参数化 T 本身。此方法还意味着不可能仅在函数 def 中执行此操作,需要预先参数化的 TypeVar T。对于未来的寻求者:打字模块文档几乎没有提到 TypeVar(bound),但 PEP 484 有更多示例。再次感谢!
猜你喜欢
  • 2022-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多