问题描述
我有一个函数可以验证其参数以仅接受给定有效选项列表中的值。在键入方面,我使用Literal
类型别名反映了此行为,如下所示:
from typing import Literal
VALID_ARGUMENTS = ['foo','bar']
Argument = Literal['foo','bar']
def func(argument: 'Argument') -> None:
if argument not in VALID_ARGUMENTS:
raise ValueError(
f'argument must be one of {VALID_ARGUMENTS}'
)
# ...
这违反了DRY原理,因为我必须重写我的文字类型定义中的有效参数列表,即使它已经存储在变量VALID_ARGUMENTS
中也是如此。 在给定Argument
变量的情况下,如何动态创建VALID_ARGUMENTS
文字类型?
以下内容不起作用:
from typing import Literal,Union,NewType
Argument = Literal[*VALID_ARGUMENTS] # SyntaxError: invalid Syntax
Argument = Literal[VALID_ARGUMENTS] # Parameters to generic types must be types
Argument = Literal[Union[VALID_ARGUMENTS]] # TypeError: Union[arg,...]: each arg must be a type. Got ['foo','bar'].
Argument = NewType(
'Argument',Union[
Literal[valid_argument]
for valid_argument in VALID_ARGUMENTS
]
) # Expected type 'Type[_T]',got 'list' instead
那么,怎么办呢?还是根本无法完成?
解决方法
反过来,从VALID_ARGUMENTS
构建Argument
:
Argument = typing.Literal['foo','bar']
VALID_ARGUMENTS: typing.Tuple[Argument,...] = typing.get_args(Argument)
可以在运行时从Argument
构建VALID_ARGUMENTS
,但这与静态分析不兼容,后者是类型注释的主要用例。从VALID_ARGUMENTS
构建Argument
就是路要走。
我在这里为VALID_ARGUMENTS
使用了一个元组,但是如果由于某种原因您确实更喜欢列表,则可以得到一个列表:
VALID_ARGUMENTS: typing.List[Argument] = list(typing.get_args(Argument))
,
如果有人仍在为此寻找解决方法:
typing.Literal[tuple(VALID_ARGUMENTS)]
,
这是解决方法。但是不知道这是否是一个好的解决方案。
VALID_ARGUMENTS = ['foo','bar']
Argument = Literal['1']
Argument.__args__ = tuple(VALID_ARGUMENTS)
print(Argument)
# typing.Literal['foo','bar']