Что означает атрибут __total__ dunder в Python 3?

17

В недавно выпущенном Python 3.8 появилась аннотация нового типа typing.TypedDict. В его документации упоминается, что

Информация о типе для самоанализа может быть доступна через Point2D.__annotations__и Point2D.__total__. [....]

Хотя __annotations__общеизвестно, что, будучи введенным в PEP 3107 , я не могу найти никакой информации о __total__. Может ли кто-нибудь объяснить его значение и, если возможно, ссылки на авторитетные источники?

Антти Хаапала
источник
4
Типичный. 99% typingвнутренних органов не документированы, а часть документирована плохо.
Аран-Фей

Ответы:

3

Я предполагаю, что __total__поле указывает, должны ли экземпляры быть полными (по умолчанию) или нет (все поля необязательны). Я начал свой поиск в PEP 589 , который представил TypedDictи описывает совокупность как таковую. В нем использовался totalаргумент, который имело бы смысл переименовать в class синтаксисе в стиле dunder . Однако я не нашел, когда произошло такое переименование.

Если посмотреть на MyPy, которая является средством проверки типов, которое заботится об этих аннотациях, то есть аналогичная документация TypedDictи совокупность , но опять же нет ссылки на синтаксис dunder. Копание в его реализации привело к еще большей путанице, так как TypedDictTypeв types.py нет общего поля, но есть отдельные itemsи required_keys. Совокупность подразумевает это, items.keys()==required_keysно реализация делает разные предположения, такие как can_be_falseполагаться на itemsодного. total=Falseдолжно в принципе значит required_keysпусто.

Исходный код CPython для _TypedDictMeta, по крайней мере, показывает, что totalаргумент и __total__dunder - одно и то же, хотя источник описывает TypedDictсебя как «может быть добавлен в ближайшее время».

Ян Вернье
источник
Принимая это сейчас - если не что-то еще, возможно, это заставит других более охотно выступить и опровергнуть ваш ответ: D
Антти Хаапала
Я лично подозреваю, can_be_falseчто это ошибка MyPy, возможно связанная с тем, что с самого начала не планировалось иметь дополнительные поля.
Ян Вернье
1

TypedDictбыл принят в Python 3.8 через PEP 589 . Начиная с Python, это __total__логический флаг, установленный Trueпо умолчанию:

tot = TypedDict.__total__
print(type(tot))
print(tot)

# <class 'bool'>
# True

Как упоминалось в других публикациях, подробности этого метода в документах ограничены , но ссылка @Yann Vernier на исходный код CPython настоятельно указывает __total__на новое totalключевое слово, введенное в Python 3.8 :

# cypthon/typing.py

class _TypedDictMeta(type):
    def __new__(cls, name, bases, ns, total=True):
        """Create new typed dict class object.
        ...
        """
        ...
        if not hasattr(tp_dict, '__total__'):
            tp_dict.__total__ = total
        ...

Как это работает?

Сводка : по умолчанию все ключи требуются при создании экземпляра определенного TypedDict. total=Falseотменяет это ограничение и разрешает дополнительные ключи. Смотрите следующую демонстрацию.

Данный

Тестовое дерево каталогов:

введите описание изображения здесь

Код

Файлы в тестовой директории:

# rgb_bad.py

from typing import TypedDict


class Color(TypedDict):
    r: int
    g: int
    b: int
    a: float


blue = Color(r=0, g=0, b=255)                     # missing "a"

# rgb_good.py

from typing import TypedDict


class Color(TypedDict, total=False):
    r: int
    g: int
    b: int
    a: float


blue = Color(r=0, g=0, b=255)                     # missing "a"

демонстрация

Если ключ отсутствует, mypy будет жаловаться в командной строке:

> mypy code/rgb_bad.py
code\rgb_bad.py:11: error: Key 'a' missing for TypedDict "Color"
...

Настройка total=Falseразрешений необязательных ключей:

> mypy code/rgb_good.py
Success: no issues found in 1 source file

Смотрите также

  • Tweet от R. Hettinger, демонстрирующий тотальность
  • Раздел PEP о совокупности в PEP 589
  • Статья Раздел о типах и TypedDictв Python 3.8 от Real Python
  • typing-extensionsпакет для использования TypedDictв Python 3.5, 3.6
pylang
источник