Tuple[Callable, Any, ...]

Опубликовано 29 января 2018 в Python

Подсказки с типами могут сильно помочь в работе с большим проектом на Питоне. Тем не менее, иногда они требуют рефакторинга кода. Я писал об этом в прошлом году в этой статье, но тогда я не смог найти хорошего примера иллюстрирующего то, что я хочу сказать.

Взгляните на этот код:

def process(workflow_step):
    func = step[0]
    args = step[1:]
    return func(*args)

Что можно понять о типах из этого кусочка? То, что workflow_step - это последовательность, первый элемент которой - Callable object, а остальные элементы, если таковые имеются, аргументы для этого вызываемого объекта. Предположим, что workflow_step - Tuple. Проблема в том, что тип (Tuple) -> Any для этой функции слишком общий и неинформативный. Можем мы вызвать ее с пустым таплом? (1, 2, 3) пройдет?

(Tuple[Callable, Any, ...]) -> Any - много лучше. Вот только, такое определение не поддерживается синтаксисом библиотеки типов Питона. В простых случаях, одним из возможных выходов из этой ситуации будет использование типов вроде таких Union[Tuple[Callable], Tuple[Callable, Any]] или чего-то подобного. В более сложных случаях единственным возможным решением будет рефакторинг.

Есть много вариантов, как это сделать. К примеру, для функции из примера я использовал NamedTuple.

class WorkflowStep(NamedTuple):
    callable  # type: Callable
    args  # type: Tuple

 def process(workflow_step):
    # type: (WorkflowStep) -> Any
    return step.callable(args. args)

Возможно, это не лучший кусок кода, который вы видели, тем не менее, он на много информативнее и структурированнее предыдущего варианта.