Каковы основные отличия NamedTuple и TypedDict в Python / mypy

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

Что касается PEP, я бы предпочел добавить общий раздел о NamedTuple и TypedDict, они очень похожи, и последний уже ведет себя структурно. Как вы думаете? источник

Но тогда Гвидо, похоже, не уверен в этом.

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

источник

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

6 ответов

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

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

Так много для "Должен быть один - и предпочтительно только один - очевидный способ сделать это".

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

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

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

from typing import NamedTuple

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

TypedDict это не часть самого Python, а экспериментальная функция Mypy, позволяющая печатать на разнородных, ориентированных на структуру словарях.

from mypy_extensions import TypedDict

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

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

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

ConnectionOptions = Dict[str, str] 

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

Я бы пошел на 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)

А TypedDict (в 3.8+)

Простое типизированное пространство имен. Во время выполнения это эквивалентно простому dict.

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

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

и (отсюда)

Подклассы NamedTuple также могут иметь строки документации и методы.

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

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

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

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

  1. Во многих случаях классы данных предлагают ряд полезных функций с меньшим объемом написания кода. Они могут быть неизменяемыми или изменяемыми, что дает нам широкий спектр возможностей.
  2. В случаях, когда данные неизменяемы, NamedTuple может быть немного более эффективным, чем замороженный класс данных, примерно на 5% — не намного. Что склоняет чашу весов здесь, так это дорогостоящее вычисление атрибутов. Хотя NamedTuple может иметь свойства, если вычисления требуют больших затрат и результаты используются часто, это может помочь вычислить их заранее, в чем NamedTuple не очень хорош. Ознакомьтесь с документацией по классам данных и их методу post_init() как по лучшему выбору в тех редких случаях, когда полезно вычислить значение атрибута заранее.
  3. Словари идеально подходят, когда полный набор ключей заранее неизвестен. Когда мы начинаем дизайн, у нас могут быть одноразовые прототипы или доказательства концепции с использованием словарей. Когда мы пытаемся писать модульные тесты и подсказки типов, нам может понадобиться повысить формальность. В некоторых случаях домен возможных ключей известен, и подсказка типа TypedDict имеет смысл как способ охарактеризовать допустимые ключи и типы значений.

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

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

Другие отличия, возможно, стоит упомянуть:

с можно использовать какtupleс, напримерfun(*userTuple),a, b = userTuple.

можно использовать какdictнапримерfun(**userDict).

Описанное выше поведение также отличается от es, где на поля обычно нужно ссылаться явно, если не прибегать кdataclasses.astuple(),.asdict().

dataclasses допускают более классовое поведение, такое как дженерики* и классовое наследование. Например, позволяет наследовать только от других s .


*: будет поддерживаться с Python 3.11 для обоихNamedTuple[ 2 ] иTypedDict[3]

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