Аргумент по умолчанию, обеспечивающий идентичность типа - PullRequest
0 голосов
/ 23 апреля 2020

У меня есть пользовательский класс (на самом деле простая оболочка для heapq), который оборачивает интерфейс в объектно-ориентированный и допускает необязательные аргументы key и cmp.

Для реализации проверки типов Я ввел Tv (тип значений в куче) и Tk (типы «ключей» значений - то есть, что будет сравниваться в куче).

Таким образом, аннотация __init__ выглядит следующим образом:

from typing import *

Tv = TypeVar('Tv')
Tk = TypeVar('Tk')

class Heap(Generic[Tv, Tk]):
    def __init__(self, initial: Optional[Iterable[Tv]],
                 key: Callable[[Tv], Tk] = lambda x: x, cmp: Callable[[Tk, Tk], bool] = op.lt):
        pass

К сожалению, mypy сообщает и ошибка в этом случае:

error: Incompatible default for argument "key" (default has type "Callable[[Tv], Tv]", argument has type "Callable[[Tv], Tk]")

Поскольку спецификация того, что является Tk, мало использовать для тех, кто собирается использовать Heap Я попытался полностью его отбросить - я сделал Heap подкласс просто Generic[Tv] и установил Tk = Any. Это имело приятный побочный эффект: пользователю Heap не нужно было указывать тип Tk, который в основном совпадает с Tv, но я потерял все проверки типов на Tk в моей реализации.

Есть ли способ сохранить проверки типов для Tk в пределах Heap и сделать значение ключа по умолчанию lambda x: x, не приводящее к ошибке?

edit: Когда я пытался сделать Heap подкласс просто Generic[Tv] и оставить Tk = TypeVar('Tk') Я все еще получил ошибку

1 Ответ

0 голосов
/ 23 апреля 2020

Предполагаемый тип lambda x: x равен Callable[[Tv], Tv], поскольку в функции нет ничего, что могло бы изменить тип x перед его возвратом. Это нарушает подсказку типа, в которой говорится, что аргумент и тип возвращаемого значения могут изменяться произвольно.

После исправления необходимо использовать cast, чтобы сообщить mypy, что функция по умолчанию может "изменить" тип своего аргумента при необходимости.

class Heap(Generic[Tv, Tk]):
    def __init__(self, 
                 initial: Optional[Iterable[Tv]],
                 key: Callable[[Tv], Tk] = lambda x: <b>cast(Tk, x)</b>,
                 cmp: Callable[[Tk, Tk], bool] = op.lt):
        pass

Однако это в основном говорит mypy, что вы знаете, что делаете, и что если функция key не указана, то вы обеспечат, чтобы значение типа Tv может фактически использоваться как значение типа Tk. В общем, есть функция no с типом Callable[[Tv], Tk] для произвольных типов Tv и Tk.

...