В чем основные различия NamedTuple и TypedDict в Python / mypy

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

Concerning the PEP, I would rather add a common section about NamedTuple and TypedDict, they are quite similar and the latter already behaves structurally. What do you think? source

Но тогда Гвидо, кажется, не так уверен в этом.

I'm not so sure that NamedTuple and TypedDict are really all that similar (except they are both attempts to handle outdated patterns in a statically-typed world).

источник

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

namedtuple и dict похожи на вас?

Azat Ibrakov 21.11.2018 10:49
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
32
1
16 713
5

Ответы 5

Python и его сообщество борются с проблемой «структуры»: как лучше всего сгруппировать связанные значения в составные объекты данных, которые обеспечивают логический / легкий доступ к компонентам (обычно по имени). Существуют конкурирующие подходы многие:

  • collections.namedtuple экземпляры
  • словари (с фиксированным / известным набором ключей)
  • словари, доступные по атрибутам (например, штучка)
  • библиотека attrs
  • PEP 557 классы данных
  • простые старые сделанные на заказ объекты, созданные вручную для каждого типа структуры
  • последовательности, такие как tuple и list, с подразумеваемыми значениями для каждой позиции / слота (архаичный, но чрезвычайно распространенный)
  • и Т. Д.

Вот и все, что «должен быть один - и желательно только один - очевидный способ сделать это».

И библиотека typing, и Mypy, как и сообщество Python в целом, одновременно борются с тем, как более эффективно определять типы / схемы, в том числе для составных объектов. Обсуждение, с которым вы связались, является частью этой борьбы и попытки найти путь вперед.

NamedTuple - это суперкласс типизации для структурированных объектов, созданных фабрикой collections.namedtuple; TypedDict Mypy пытается определить ключи и соответствующие типы значений, которые возникают при использовании словарей с фиксированной схемой. Они похожи, если вы просто думаете: «У меня есть фиксированный набор ключей, который должен соответствовать фиксированному набору типизированных значений». Но результирующие реализации и ограничения очень разные. Сумка и коробка похожи? Может быть. Может быть нет. Зависит от вашей точки зрения и от того, как вы хотите их использовать. Налей вина и пусть начнется обсуждение!

Кстати, NamedTuple теперь является формальной частью Python.

from typing import NamedTuple

class Employee(NamedTuple):
    name: str
    id: int

TypedDict зародился как экспериментальная функция Mypy, призванная превратить набор текста в разнородное, структурно-ориентированное использование словарей. Однако начиная с Python 3.8 он был включен в стандартную библиотеку.

try:
    from typing import TypedDict  # >=3.8
except ImportError:
    from mypy_extensions import TypedDict  # <=3.7

Movie = TypedDict('Movie', {'name': str, 'year': int})

Конструктор типа на основе классов также доступен:

class Movie(TypedDict):
    name: str
    year: int

Несмотря на их различия, как NamedTuple, так и TypedDict блокируют определенные ключи, которые будут использоваться, и типы значений, соответствующие каждому ключу. Следовательно, они преследуют одну и ту же цель: быть полезными механизмами типизации для составных / структурных типов.

Стандарт Python typing.Dict фокусируется на гораздо более однородных параллельных сопоставлениях, определяя типы ключ / значение, а не ключи как таковой. Поэтому он не очень полезен при определении составных объектов, которые хранятся в словарях.

ConnectionOptions = Dict[str, str] 

Спасибо за отзыв. Вы сознательно пошли на синтаксис TypedDict? Потому что есть также синтаксис на основе классов, который делает их похожими на NamedTuple (с точки зрения синтаксиса)

Christoph 22.11.2018 15:45

Я использовал синтаксис документация Mypy. Документы обычно являются лучшим источником того, что считается каноническим / предпочтительным.

Jonathan Eunice 22.11.2018 15:57

Обновление: TypedDict теперь является частью стандартной библиотеки, начиная с Python 3.8! docs.python.org/3/library/typing.html#typing.TypedDict

Kevin Languasco 16.10.2019 04:31

@KevinLanguasco Спасибо за обновление. Исправленный ответ для размещения.

Jonathan Eunice 16.10.2019 10:30

Думаю, в основном обсуждались сходства, а различий не было.

Gonzalo 16.10.2019 21:27

NamedTuple - это особый тип. Как следует из названия, это кортеж, который расширен за счет именованных записей.

TypedDict не является реальным объектом, вы не можете (или, по крайней мере, не должны) использовать его, вместо этого он используется для добавления информации о типе (для mypy type checker) для аннотирования типов в сценариях, когда в словаре есть разные ключи с разными типами, т.е. практически везде, где следует использовать NamedTuple. Очень полезно аннотировать существующий код, который вы не хотите подвергать рефакторингу.

TypedDict (в 3.8+)

A simple typed namespace. At runtime it is equivalent to a plain dict.

тогда как NamedTuple является «подклассом кортежа». Обратите внимание, что

Named tuple instances do not have per-instance dictionaries, so they are lightweight and require no more memory than regular tuples.

и (отсюда)

NamedTuple subclasses can also have docstrings and methods

Проще говоря, NamedTuple больше похож на настраиваемый объект, а TypedDict больше похож на типизированный словарь.

Я не проверял, но, судя по этим описаниям, я ожидал, что NamedTuples будет иметь некоторые (небольшие) преимущества по времени выполнения и памяти по сравнению с TypedDict.

Однако, если вы используете API, например, который ожидает dict, TypedDict может быть предпочтительнее, поскольку это dict (хотя вы также можете создать dict из NamedTuple с помощью его метода _asdict()).

Есть пара незначительных отличий. Обратите внимание, что эти контейнеры не были там вечно:

Я бы выбрал NamedTuple, если это возможно, и если я хочу, чтобы значения были заморожены. В противном случае я бы использовал класс данных.

from dataclasses import dataclass
from typing import NamedTuple, TypedDict
from enum import Enum


class Gender(Enum):
    MALE = "male"
    FEMALE = "female"


## Class definition: Almost the same
@dataclass
class UserDataC:
    name: str
    gender: Gender


class UserTuple(NamedTuple):
    name: str
    gender: Gender


class UserNDict(TypedDict):
    name: str
    gender: Gender


## Object Creation: Looks the same
anna_datac = UserDataC(name = "Anna", gender=Gender.FEMALE)
anna_tuple = UserTuple(name = "Anna", gender=Gender.FEMALE)
anna_ndict = UserNDict(name = "Anna", gender=Gender.FEMALE)

## Mutable values vs frozen values
anna_datac.gender = Gender.MALE
# anna_tuple.gender = Gender.MALE  # AttributeError: can't set attribute
anna_ndict["gender"] = Gender.MALE
# AttributeError: 'dict' object has no attribute 'gender'
# anna_ndict.gender = Gender.MALE

## New attribute
# Note that you can add new attributes like this.
# Python will not complain. But mypy will.
anna_datac.password = "secret"  # Dataclasses are extensible
# anna_tuple.password = "secret"  # AttributeError - named tuples not
# anna_ndict.password = "secret"  # AttributeError - TypedDict not
anna_ndict["password"] = "secret"

## isinstance
assert isinstance(anna_tuple, tuple)
assert isinstance(anna_ndict, dict)

Почему я предпочитаю NamedTuple, а не namedtuple

Я считаю, что писать и читать проще. Кроме того, вы даете mypy больше возможностей для проверки:

class UserTuple(NamedTuple):
    name: str
    gender: Gender

# vs

UserTuple = namedtuple("UserTuple", ["name", "gender"])

Почему я предпочитаю кортежи словарям

Если мне не нужно, чтобы что-то было изменяемым, мне нравится, если это не так. Таким образом я предотвращаю неожиданные побочные эффекты

Вы можете объяснить, почему вы выбрали NamedTuple?

pir 10.11.2020 20:46

Из отличной книги Стивена Ф. Лотта и Дасти Филлипса «Объектно-ориентированное программирование на Python»

  1. For a lot of cases, dataclasses offer a number of helpful features with less code writing. They can be immutable, or mutable, giving us a wide range of options.
  2. For cases where the data is immutable, a NamedTuple can be slightly more efficient than a frozen dataclass by about 5% – not much. What tips the balance here is an expensive attribute computation. While a NamedTuple can have properties, if the computation is very costly and the results are used frequently, it can help to compute it in advance, something a NamedTuple isn't good at. Check out the documentation for dataclasses and their post_init() method as a better choice in the rare case where it's helpful to compute an attribute value in advance.
  3. Dictionaries are ideal when the complete set of keys isn't known in advance. When we're starting a design, we may have throwaway prototypes or proofs of concept using dictionaries. When we try to write unit tests and type hints, we may need to ramp up the formality. In some cases, the domain of possible keys is known, and a TypedDict type hint makes sense as a way to characterize the valid keys and value types.

Другие вопросы по теме