Вопрос-Ответ

What are "named tuples" in Python?

Что такое "именованные кортежи" в Python?

  • Что такое именованные кортежи и как их использовать?

  • Когда я должен использовать именованные кортежи вместо обычных или наоборот?

  • Существуют ли также "именованные списки"? (т. е. Изменяемые именованные кортежи)


Конкретно по последнему вопросу смотрите также Существование изменяемого именованного кортежа в Python?.

Переведено автоматически
Ответ 1

Именованные кортежи - это, в основном, простые в создании, легковесные типы объектов. Ссылки на экземпляры именованных кортежей можно использовать объектно-подобное разыменование переменных или стандартный синтаксис кортежей. Они могут использоваться аналогично struct или другим распространенным типам записей, за исключением того, что они неизменяемы. Они были добавлены в Python 2.6 и Python 3.0, хотя есть рецепт для реализации в Python 2.4.

Например, точку принято представлять в виде кортежа (x, y). Это приводит к коду, подобному следующему:

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)

Использование именованного кортежа становится более читабельным:

from collections import namedtuple
Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)

Однако именованные кортежи по-прежнему обратно совместимы с обычными кортежами, поэтому следующее все равно будет работать:

Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
# use index referencing
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
# use tuple unpacking
x1, y1 = pt1

Таким образом, вам следует использовать именованные кортежи вместо кортежей везде, где, по вашему мнению, объектная нотация сделает ваш код более питонистским и более легко читаемым. Я лично начал использовать их для представления очень простых типов значений, особенно при передаче их в качестве параметров функциям. Это делает функции более удобочитаемыми, не видя контекста упаковки кортежей.

Кроме того, вы также можете заменить обычные неизменяемые классы, у которых нет функций, только поля с ними. Вы даже можете использовать свои именованные типы кортежей в качестве базовых классов:

class Point(namedtuple('Point', 'x y')):
[...]

Однако, как и в случае с кортежами, атрибуты в именованных кортежах неизменяемы:

>>> Point = namedtuple('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
AttributeError: can't set attribute

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

>>> from rcdtype import *
>>> Point = recordtype('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
>>> print(pt1[0])
2.0

Однако я не знаю ни о какой форме "именованного списка", которая позволяет добавлять новые поля. Возможно, в этой ситуации вам просто захочется использовать словарь. Именованные кортежи могут быть преобразованы в словари с помощью pt1._asdict() который возвращает {'x': 1.0, 'y': 5.0}, и с ними можно работать с помощью всех обычных функций словаря.

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

Ответ 2

Что такое именованные кортежи?


Именованный кортеж - это кортеж.

Он делает все, что может кортеж.

Но это больше, чем просто кортеж.

Это определенный подкласс кортежа, который программно создается в соответствии с вашей спецификацией, с именованными полями и фиксированной длиной.

Это, например, создает подкласс tuple , и помимо того, что он имеет фиксированную длину (в данном случае три), его можно использовать везде, где используется кортеж, без разрыва. Это известно как заменяемость по Лискову.

Новое в Python 3.6, мы можем использовать определение класса с typing.NamedTuple для создания namedtuple:

from typing import NamedTuple

class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list

Приведенное выше аналогично collections.namedtuple, за исключением того, что приведенное выше дополнительно содержит аннотации типов и строку документации. Приведенное ниже доступно в Python 2+:

>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)

Это создает его экземпляр:

>>> ant = ANamedTuple(1, 'bar', [])

Мы можем проверить это и использовать его атрибуты:

>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']

Более подробное объяснение

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

Вот как вы могли бы использовать обычный кортеж:

>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'

Вы можете расширить кортеж с помощью итеративной распаковки:

>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'

Именованные кортежи - это кортежи, которые позволяют получать доступ к своим элементам по имени, а не просто по индексу!

Вы создаете namedtuple следующим образом:

>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])

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

>>> Student = namedtuple('Student', 'first last grade')

Как их использовать?


Вы можете делать все, что могут делать кортежи (см. Выше), а также выполнять следующее:

>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
>>> named_student_tuple.grade
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple = named_student_tuple._replace(first='Bart', grade='C')
>>> new_named_student_tuple
Student(first='Bart', last='Simpson', grade='C')

Один из комментаторов спросил:


Где обычно определяется именованный кортеж в большом скрипте или программе?


Типы, которые вы создаете с помощью namedtuple, в основном представляют собой классы, которые вы можете создавать с помощью простого сокращения. Относитесь к ним как к классам. Определите их на уровне модуля, чтобы pickle и другие пользователи могли их найти.

Рабочий пример на уровне глобального модуля:

>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
>>> pickle.loads(pickle.dumps(nt))
NT(foo='foo', bar='bar')

И это демонстрирует невозможность поиска определения:

>>> def foo():
... LocalNT = namedtuple('LocalNT', 'foo bar')
... return LocalNT('foo', 'bar')
...
>>> pickle.loads(pickle.dumps(foo()))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed

Почему / когда я должен использовать именованные кортежи вместо обычных?


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

Вы можете использовать их вместо объекта, если в противном случае вы использовали бы объект с неизменяемыми атрибутами данных и без какой-либо функциональности.

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

class Point(namedtuple('Point', 'x y')):
"""adding functionality to a named tuple"""
__slots__ = ()
@property
def hypot(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)

Почему / когда я должен использовать обычные кортежи вместо именованных?


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

Именованные кортежи не используют дополнительной памяти по сравнению с кортежами.


Существует ли какой-либо "именованный список" (изменяемая версия именованного кортежа)?


Вы ищете либо щелевой объект, который реализует все функциональные возможности списка статического размера, либо подклассный список, который работает как именованный кортеж (и который каким-то образом блокирует изменение размера списка).

Теперь расширенный и, возможно, даже заменяемый по Лискову пример первого:

from collections import Sequence

class MutableTuple(Sequence):
"""Abstract Base Class for objects that work like mutable
namedtuples. Subclass and define your named fields with
__slots__ and away you go.
"""

__slots__ = ()
def __init__(self, *args):
for slot, arg in zip(self.__slots__, args):
setattr(self, slot, arg)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
# more direct __iter__ than Sequence's
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
# Sequence requires __getitem__ & __len__:
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)

И для использования просто создайте подкласс и определите __slots__:

class Student(MutableTuple):
__slots__ = 'first', 'last', 'grade' # customize


>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
...
Bart
Simpson
A
Ответ 3

namedtuple - это фабричная функция для создания класса tuple. С помощью этого класса мы можем создавать кортежи, которые также можно вызывать по имени.

import collections

#Create a namedtuple class with names "a" "b" "c"
Row = collections.namedtuple("Row", ["a", "b", "c"])

row = Row(a=1,b=2,c=3) #Make a namedtuple from the Row class we created

print row #Prints: Row(a=1, b=2, c=3)
print row.a #Prints: 1
print row[0] #Prints: 1

row = Row._make([2, 3, 4]) #Make a namedtuple from a list of values

print row #Prints: Row(a=2, b=3, c=4)
Ответ 4

именованные кортежи - отличная функция, они являются идеальным контейнером для данных. Когда вам нужно "хранить" данные, вы бы использовали кортежи или словари, например:

user = dict(name="John", age=20)

или:

user = ("John", 20)

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

namedtuples - идеальный компромисс для двух подходов, они обладают отличной читабельностью, малым весом и неизменяемостью (плюс они полиморфны!).

python