Как называются кортежи?
Именованный кортеж - это кортеж.
Он делает все, что может кортеж.
Но это больше, чем просто кортеж.
Это определенный подкласс кортежа, который программно создается по вашей спецификации с именованными полями и фиксированной длиной.
Это, например, создает подкласс кортежа, и помимо фиксированной длины (в данном случае три), он может использоваться везде, где кортеж используется без разрывов. Это называется заменяемостью по Лискову:
>>> 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'])
Вы также можете использовать одну строку с именами, разделенными пробелами, чуть более удобочитаемое использование 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