Python3 typehints - 传入类型元组后,指定返回值是这些类型实例的元组

Posted

技术标签:

【中文标题】Python3 typehints - 传入类型元组后,指定返回值是这些类型实例的元组【英文标题】:Python3 typehints - After passing in a Tuple of Types, specify that the return value is a Tuple of instances of those Types 【发布时间】:2018-12-29 14:12:37 【问题描述】:

我目前使用一些复杂的类型提示来实现我使用sqlalchemy 为 dB 调用所做的“快捷方式”功能。例如,不必写,

users: List[User] = User.query.all()

我在我的基础模型中创建了一个快捷函数,

@classmethod
def qall(cls: Type[T_DBModel]) -> List[T_DBModel]:
    cls.query.all()

所以现在我可以只写 users = User.qall() 并且 typehint 就像我的 IDE (PyCharm) 的魅力一样 - 它可以正确识别这将是 List[User] 而无需我指定。

现在,我想对sqlalchemywith_entities 函数做同样的事情。有了它,您可以指定您希望 dB 调用返回一个元组,即,

users_roles: List[Tuple[User, Role]] = User.query.with_entities(User, Role).all()

我想为此写一个与上面类似的快捷方法,

@classmethod
def qwith_entities(cls: Type[T_DBModel], retvals: T) -> List[T]:
    cls.query.with_entities(*retvals).all()

所以我可以用同样的方式“快捷”使用它,

users_roles = User.qwith_entities((User, Role))

问题在于,使用该签名,而不是 typehint 指定返回 List[Tuple[User, Role]],它是 List[Tuple[Type[User], Type[Role]]],这是不正确的,也不是返回值。

如果我把它分解成多个函数,那就很好了,

@classmethod
def qwith_entity(cls: Type[T_DBModel], retval: Type[T]) -> List[T]:
    cls.query.with_entities(retval).all()

它知道Base.qwith_entity(User) 将是List[User](你永远不会这样做,只是一个例子)并且

@classmethod
def qwith_2_entities(cls: Type[T_DBModel], retval_1: Type[T], retval_2: Type[Y]) -> List[Tuple[T, Y]]:
    cls.query.with_entities(retval_1, retval_2).all()

@classmethod
def qwith_2_entities(cls: Type[T_DBModel], retvals: Tuple[Type[T], Type[Y]]) -> List[Tuple[T, Y]]:
    cls.query.with_entities(*retvals).all()

谁知道users_roles = Base.qwith_2_entities((User, Role)) 将是List[Tuple[User, Role]]

但我不知道怎么做,也不知道是否可以正确键入不同 Tuplevariable 长度 Types,并且返回值应该解压它,即一些愚蠢的东西喜欢,

@classmethod
def qwith_entities(cls: Type[T_DBModel], retvals: Tuple[Type[...T]]) -> List[Tuple[T]]:
    cls.query.with_entities(*retvals).all()

【问题讨论】:

【参考方案1】:

您需要variadic generics 才能准确拼出签名; 这还不可能,但它可能看起来像

Ts = TypeVar('Ts', variadic=True)

@classmethod
def qwith_2_entities(cls: Type[T_DBModel], retvals: Tuple[Type[Ts], ...]) -> List[Tuple[Ts, ...]]:
    cls.query.with_entities(*retvals).all()

其中TsTypeVar 是可变参数,Tuple[Type[Ts], ...] 扩展为Tuple[Type[T_0], Type[T_1], Type[T_2], ...]),返回列表中的类型扩展为Tuple[T_0, T_1, T_2, ...])

但是,如上所述,这还不是可用的语法。解决方法是使用 @overloads 和 1、2、3 等不同的 TypeVars,至少足以满足所有用例:

T1 = TypeVar('T1')
T2 = TypeVar('T2')
T3 = TypeVar('T3')
T4 = TypeVar('T4')
# add more as needed

@overload
def qwith_2_entities(cls: Type[T_DBModel], retvals: Tuple[Type[T1]]) -> List[Tuple[T1]]:
    pass
@overload
def qwith_2_entities(cls: Type[T_DBModel], retvals: Tuple[Type[T1], Type[T2]]) -> List[Tuple[T1, T2]]:
    pass
@overload
def qwith_2_entities(cls: Type[T_DBModel], retvals: Tuple[Type[T1], Type[T2], Type[T3]]) -> List[Tuple[T1, T2, T3]]:
    pass
@overload
def qwith_2_entities(cls: Type[T_DBModel], retvals: Tuple[Type[T1], Type[T2], Type[T3], Type[T4]]) -> List[Tuple[T1, T2, T3, T4]]:
    pass
# add more overloads as needed
@classmethod
def qwith_2_entities(cls, retvals):
    cls.query.with_entities(*retvals).all()

【讨论】:

以上是关于Python3 typehints - 传入类型元组后,指定返回值是这些类型实例的元组的主要内容,如果未能解决你的问题,请参考以下文章

python 函数定义冒号?箭头?啥意思?

python限定方法参数类型返回值类型变量类型等

为啥没有在 python3 中检查返回类型? [复制]

python2和python3的差异

Python3中typing模块介绍

Python3中装饰器@typing.overload的使用