dataclasses.Field не разрешает аннотацию типа к фактическому типу - PullRequest
0 голосов
/ 01 мая 2019

Документация для Field класса стандартного Python модуля dataclasses указывает только:

Его документированные атрибуты:

  • [...]
  • type: тип поля.

Мне кажется, это означает, что поле будет содержать сам тип, а не только его имя в виде строки.

Однако, похоже, что он просто копирует аннотацию типа как есть, что делает его совершенно бесполезным.

Пример: * * один тысяча двадцать-одна

@dataclasses.dataclass 
class C: 
    c: 'C'

dataclasses.fields(C)[0].type # This returns the string 'C'
typing.get_type_hints(C)['c'] # This returns the class C, as expected

Проблема возникает даже систематически при использовании аннотаций типа PEP563 .

Это ошибка в модуле dataclasses? Это ожидаемое поведение? Если да, то как мне получить объект типа с учетом экземпляра поля?

1 Ответ

2 голосов
/ 01 мая 2019

Это умышленно.Разрешение подсказок типа во время импорта стоит дорого , особенно когда from __future__ import annotations использовалось, чтобы отключить их разрешение в первую очередь.

Первоначально добавление PEP 563 в Python 3.7 прервалоськлассы данных, когда вы использовали переключатель from __future__ import annotations и включали аннотации типа ClassVar или InitVar для полей;они не будут решены на этом этапе и останутся строкой.Это уже было проблемой до PEP 563, если вы явно использовали строки, см. выпуск классов данных # 92 .Эта ошибка стала Python, # 33453 , когда классы данных превратили ее в собственный Python 3.7.

Родительский проект, attrs, который также вдохновил dataclasses, пришлось решить эту проблему .Там, Лукаш Ланга (соавтор большинства типов хинтинга, включая PEP 563), заявляет:

ОК, поэтому я попробовал вышеизложенное, и кажется, что это ядерный вариант, так как он заставляет всеаннотации для оценки.Это то, чего я хотел избежать с помощью from __future__ import annotations.

и в обсуждении по запросу на удаление, который исправил проблему 33453 , Эрик Смит, автор dataclasses, заявил:

Я занимаюсь именно этим.Я думаю, что точка зрения @ ambv заключается в том, что она вводит снижение производительности из-за вызова eval для каждого поля, тогда как смысл строковых аннотаций состоит в том, чтобы удалять снижение производительности.

Кроме того, были другие проблемы;Вы не можете оценивать все подсказки типов во время импорта, а не тогда, когда они используют прямые ссылки:

В дополнение к проблеме производительности, в следующем случае (без оператора __future__ и без классов данных)Я получаю сообщение об ошибке get_type_hints(), потому что C не определено при вызове get_type_hints().Это python / набрав # 508 .Обратите внимание, что там, где в этом примере вызывается get_type_hints(), именно там и будет выполняться @dataclass, и ему нужно будет вызвать урезанное get_type_hints().

Итак, в конце концов, все это dataclasses делает - это делает, применяет эвристику строк к аннотациям и не загружает их для вас.

Чтобы получить тип, просто используйте get_type_hints() в самом классе и используйте атрибут field .nameкак ключ к результату:

resolved = typing.get_type_hints(C)
f = dataclasses.fields(C)[0]
ftype = resolved[f.name]
...