Каковы основные отличия 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]
Есть пара незначительных отличий. Обратите внимание, что эти контейнеры не были там вечно:
- PEP 557 - Классы данных: Python 3.7
- collection. namedtuple: Python 3?
- typing.NamedTuple: Python 3.6?
- PEP 589 - TypedDict
Я бы пошел на 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" .
- Во многих случаях классы данных предлагают ряд полезных функций с меньшим объемом написания кода. Они могут быть неизменяемыми или изменяемыми, что дает нам широкий спектр возможностей.
- В случаях, когда данные неизменяемы, NamedTuple может быть немного более эффективным, чем замороженный класс данных, примерно на 5% — не намного. Что склоняет чашу весов здесь, так это дорогостоящее вычисление атрибутов. Хотя NamedTuple может иметь свойства, если вычисления требуют больших затрат и результаты используются часто, это может помочь вычислить их заранее, в чем NamedTuple не очень хорош. Ознакомьтесь с документацией по классам данных и их методу post_init() как по лучшему выбору в тех редких случаях, когда полезно вычислить значение атрибута заранее.
- Словари идеально подходят, когда полный набор ключей заранее неизвестен. Когда мы начинаем дизайн, у нас могут быть одноразовые прототипы или доказательства концепции с использованием словарей. Когда мы пытаемся писать модульные тесты и подсказки типов, нам может понадобиться повысить формальность. В некоторых случаях домен возможных ключей известен, и подсказка типа TypedDict имеет смысл как способ охарактеризовать допустимые ключи и типы значений.
NamedTuple
это определенный тип. Как следует из названия, это кортеж, который расширен, чтобы иметь именованные записи.
TypedDict
это не реальный объект, вы не можете (или, по крайней мере, не должны) его использовать, вместо этого он используется для добавления информации о типе (для средства проверки типов mypy) для аннотирования типов в сценариях, когда в словаре есть различные ключи с различными типами, т.е. по существу все места, когда нужно использовать NamedTuple
, Очень полезно комментировать существующий код, который вы не хотите реорганизовывать.
Другие отличия, возможно, стоит упомянуть:
с можно использовать какtuple
с, напримерfun(*userTuple)
,a, b = userTuple
.
можно использовать какdict
напримерfun(**userDict)
.
Описанное выше поведение также отличается от es, где на поля обычно нужно ссылаться явно, если не прибегать кdataclasses.astuple()
,.asdict()
.
dataclass
es допускают более классовое поведение, такое как дженерики* и классовое наследование. Например, позволяет наследовать только от других s .
*: будет поддерживаться с Python 3.11 для обоихNamedTuple
[ 2 ] иTypedDict
[3]