Статическая печать сложна.mypy
может определить, что значения dictionary
не все имеют одинаковый тип, но это далеко.Тип static dictionary
равен Dict[str,object]
, в зависимости от начального значения.Тем не менее, mypy
не пытается моделировать код дальше, что означает, что он понятия не имеет, если d['sub_dict']
равно все еще другому dict
в точке, где вы пытаетесь проиндексировать его с помощью key2
, что приводит к ошибке типа.
Одна вещь, которую вы можете сделать, - это помочь mypy
, сказав, что определенное значение можно рассматривать как имеющий определенный тип, используя typing.cast
.
print(typing.cast(typing.Dict[str,dict], d['sub_dict'])['key2'])
Во время выполнения typing.cast
фактически является функцией идентификации;он просто возвращает свой второй аргумент.mypy
рассматривает его как более сильный тип подсказки, говоря, что независимо от любых предыдущих подсказок или аннотаций, d['sub_dict']
следует рассматривать как Dict[str,dict]
.
Обратите внимание, что при использовании cast
,вы говорите mypy
, что вы берете на себя ответственность за то, чтобы dictionary['sub_dict']
на самом деле был dict
во время выполнения, поскольку это не то, что вы можете передать статическим типом.Вы можете подумать, что что-то вроде
dictionary : Dict[str,Union[int,dict]] = ...
будет работать, но это просто говорит mypy
, что было бы ошибкой типа написать dictionary['foo'] = 'bar'
, поскольку 'bar'
не является ни int
, ниdict
.Даже с более точной подсказкой типов у mypy
нет возможности узнать, к какому типу значения dictionary
соответствует какой-либо конкретный ключ.
Вы также можете использовать Any
:
dictionary: Dict[str,Any] = ...
потому что теперь вы говорите, что в качестве значения можно использовать любой тип, и что любой тип может быть принят в качестве результата индексации, и эти два типа не должны быть выстроены в линию,То есть, dictionary['key1'] = 3
хорошо, потому что int
совместимо с Any
, но dictionary['sub_dict']['key2']
также хорошо, потому что то, что производит dictionary['sub_dict']
, также совместимо с Any
, и вы можете предположить, чточто этот тип сам по себе индексируется.По сути, он охватывает любое использование dictionary
в любом месте вашего кода, а не в конкретном месте, где вы использовали cast
, чтобы утверждать, что должно быть разрешено.
Большое отступление: существует понятие зависимых типов , простейшим примером которого является тип, подобный PositiveInt
, который будет идентичен int
, за исключением того, что он не допускает отрицательных значений.Казалось бы, dictionary
имеет похожий зависимый тип, где тип значений действительно является функцией фактических данных, хранящихся в значении.Например, представьте, что вы могли бы использовать экземпляр из dict
с Dict
для указания типа его значений.
dictionary: Dict[str, {"key1": int, "sub_dict": dict}] = {'key1': 1,
'sub_dict': {'key2': 0}
}
Теперь не только mypy
может сказатьчто dictionary['key1']
должен быть int
, но сам по себе dictionary
никогда не может иметь никаких ключей , кроме , чем key1
и sub_dict
.(И в этом гипотетическом мире defaultdict
может сопоставить произвольный неуказанный ключ типу по умолчанию.)