Есть ли ковариантная изменяемая версия List? - PullRequest
2 голосов
/ 09 июля 2020

Я сократил код, который я действительно хочу аннотировать для этой минимальной версии:

def print_it(numbers_or_nones):
    for i, number in enumerate(numbers_or_nones):
        if number is None:
            numbers_or_nones[i] = 0
            print("NOOOO")
        else:
            print(number)


numbers = [1, 2, 3, 4]
print_it(numbers)

Я хочу аннотировать параметр numbers_or_nones из print_it. Он должен быть ...

  • ... универсальным c типом, где элементы Optional[int]
  • ... итерируемые
  • .. . support индексированное присвоение

Какой правильный тип для этого случая? Обратите внимание, что изменить тип numbers : List[int] невозможно. Единственный вариант, который я вижу, - это использовать typing.overload.

List

Самый простой вариант - List[Optional[int]]. Однако это дает:

error: Argument 1 to "print_it" has incompatible type "List[int]"; expected "List[Optional[int]]"
note: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance
note: Consider using "Sequence" instead, which is covariant

Последовательность

Unsupported target for indexed assignment ("Sequence[Optional[int]]")

MutableSequence

error: Argument 1 to "print_it" has incompatible type "List[int]"; expected "MutableSequence[Optional[int]]"

1 Ответ

1 голос
/ 12 июля 2020

Краткий ответ: ковариантных изменяемых коллекций нет, здесь перечислены возможные стратегии решения этой ситуации https://mypy.readthedocs.io/en/stable/common_issues.html#invariance -vs-covariance

Почему изменяемые коллекции не могут быть ковариантными? Поскольку это может привести к серьезным проблемам, например:

def make_first_None(numbers_or_nones: MutableSequence[Optional[int]]):
    numbers_or_nones[0] = None


numbers: List[int] = [1, 2, 3, 4]
make_first_None(numbers) # Error!! Numbers is not a List[int] anymore!!!

Более подробное объяснение того, почему изменяемые коллекции должны быть инвариантными, находится здесь https://mypy.readthedocs.io/en/stable/generics.html#variance -of-generics

В этом случае три стратегии, перечисленные в документации, будут выглядеть так:

  • Используйте явную аннотацию типа:
numbers: List[Optional[int]] = [1, 2, 3, 4]
print_it(numbers)
  • Сделайте скопируйте и передайте это
print_it(list(numbers))
  • Используйте неизменяемые коллекции
def print_it(numbers_or_nones: Sequence[Optional[int]]) -> List[int]:
    for number in numbers:
        if number is None:
            print("NOOOO")
        else:
            print(number)
    return [number or 0 for number in numbers]


numbers = [1, 2, 3, 4]
print_it(numbers)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...