«Неподдерживаемая цель для индексированного назначения» с mypy, в зависимости от хинтинга типа относительно назначения - PullRequest
2 голосов
/ 28 февраля 2020

Я пытаюсь набрать код python и получил следующую ошибку mypy: «Неподдерживаемая цель для индексированного назначения»

В упрощенном примере это соответствует следующему коду :

from pathlib import Path
from typing import (Literal, Mapping,
                    Optional, Union)

STRAND = Literal["+", "-"]
PATH = Union[str, Path]
fastq_files: Mapping[STRAND, Optional[PATH]] = {  # simultaneous annotation and assignment
    "+": None,
    "-": None}

reads_dir = Path("/tmp")
fastq_files["+"] = reads_dir.joinpath(  # mypy error
    "plus.fastq.gz")
fastq_files["-"] = reads_dir.joinpath(  # mypy error
    "minus.fastq.gz")

Ошибка возникает при замене None на Path в значениях словарей.

Почему значения типа Optional[PATH] нельзя заменить значениями типа Path, учитывая, что PATH равно Union[str, Path]? Я бы подумал, что Path совместим с Union[str, Path], что, в свою очередь, совместимо с Optional[Union[str, Path]].

И почему ошибка исчезает, когда я комментирую dict перед назначением вместо того, чтобы комментировать его время назначения (см. ниже)?

from pathlib import Path
from typing import (Literal, Mapping,
                    Optional, Union)

STRAND = Literal["+", "-"]
PATH = Union[str, Path]
fastq_files: Mapping[STRAND, Optional[PATH]]  # annotation before assignment
fastq_files = {
    "+": None,
    "-": None}

reads_dir = Path("/tmp")
fastq_files["+"] = reads_dir.joinpath(  # no mypy error
    "plus.fastq.gz")
fastq_files["-"] = reads_dir.joinpath(  # no mypy error
    "minus.fastq.gz")

Выше показано, что None можно заменить на Path в «слоте» с типом Optional[Union[str, Path]].

это значит, что когда я делаю аннотацию одновременно с назначением, фактический тип «сокращается» до самого строгого из возможных типов, совместимых с присвоенным значением? (вследствие чего «слот» приобретает более ограничительный тип)

Ответы [ 2 ]

2 голосов
/ 28 февраля 2020

Проблема в том, что Mapping должен быть протоколом только для чтения - если вы проверите подсказки типа для Mapping , вы увидите, что он буквально не определяет метод __setitem__.

Если вы хотите иметь возможность изменять свое отображение, вам нужно вместо этого использовать Dict или MutableMapping.

Переключение на использование TypedDict, как предлагается в другом ответе, также будет работать, поскольку TypedDicts предполагается, что является подтипом Dict и поэтому является изменчивым.

1 голос
/ 28 февраля 2020

Я думаю TypedDict решает эту (странную) проблему:

from pathlib import Path
from typing import (Literal, Mapping,
                    Optional, Union, TypedDict)

STRAND = Literal["+", "-"]
PATH = Union[str, Path]
FASTQ = TypedDict("FASTQ", {"+": Optional[PATH], "-": Optional[PATH]})

fastq_files: FASTQ = {
    "+": None,
    "-": None}

reads_dir = Path("/tmp")
fastq_files["+"] = reads_dir.joinpath("plus.fastq.gz")
fastq_files["-"] = reads_dir.joinpath("minus.fastq.gz")
...