Tengo una función que valida su argumento para aceptar solo valores de una lista dada de opciones válidas. En cuanto a la escritura, reflejo este comportamiento usando un alias de tipo Literal , así:
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}' ) # ... Esto es una violación del principio DRY, porque tengo que volver a escribir la lista de argumentos válidos en la definición de mi tipo Literal, incluso si ya está almacenada en la variable VALID_ARGUMENTS . ¿Cómo puedo crear dinámicamente el tipo Argument Literal, dada la variable VALID_ARGUMENTS ?
Las siguientes cosas no funcionan:
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' insteadEntonces, ¿cómo se puede hacer? ¿O no se puede hacer en absoluto?
Vaya al revés y VALID_ARGUMENTS a partir de Argument :
Argument = typing.Literal['foo', 'bar'] VALID_ARGUMENTS: typing.Tuple[Argument, ...] = typing.get_args(Argument) Es posible en tiempo de ejecución crear Argument a partir de VALID_ARGUMENTS , pero hacerlo es incompatible con el análisis estático, que es el caso de uso principal de las anotaciones de tipo. Construir VALID_ARGUMENTS a partir de Argument es el camino a seguir.
He usado una tupla para VALID_ARGUMENTS aquí, pero si por alguna razón realmente prefiere una lista, puede obtener una:
VALID_ARGUMENTS: typing.List[Argument] = list(typing.get_args(Argument))Aquí está la solución para esto. Pero no sé si es una buena solución.
VALID_ARGUMENTS = ['foo', 'bar'] Argument = Literal['1'] Argument.__args__ = tuple(VALID_ARGUMENTS) print(Argument) # typing.Literal['foo', 'bar']Si alguien todavía está buscando una solución para esto:
typing.Literal[tuple(VALID_ARGUMENTS)]