Python输入:从有效值列表中动态创建文字别名

问题描述

我有一个函数可以验证其参数以仅接受给定有效选项列表中的值。在键入方面,我使用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']