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

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

P. S. Это перевод вопроса c enSO. Интересующихся темой "зачем делать такие переводы" прошу сюда.


Ответы (1 шт):

Автор решения: Глеб

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

Именованные кортежи — это кортежи.

Они могут делать все, что и обычные кортежи.

Но они — больше чем кортежи.

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

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

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

from typing import NamedTuple

class ANamedTuple(NamedTuple):
    """строка документации"""
    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'

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

Например, можно создать такой именованный кортеж:

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

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

>>> 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')

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

Их можно использовать вместо объекта (если бы в противном случае вы использовали бы объект с неизменяемыми атрибутами и без функций).

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

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)
→ Ссылка