Як називаються кортежі?
Названий кортеж - кортеж.
Це робить усе, що може кортеж.
Але це більше, ніж просто кортеж.
Це специфічний підклас кортежу, програмно створений відповідно до вашої специфікації, із названими полями та фіксованою довжиною.
Наприклад, це створює підклас кортежу, окрім того, що має фіксовану довжину (у цьому випадку три), він може бути використаний скрізь, коли кортеж використовується без розриву. Це відома як заміна Ліскова.
Нове в Python 3.6 , ми можемо використовувати визначення класу,typing.NamedTuple
щоб створити nametuple:
from typing import NamedTuple
class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list
Вищенаведене - те саме, що наведено нижче, за винятком того, що вище вказані додаткові анотації та докстринг Нижче наведено в 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'])
Ви також можете використовувати один рядок з іменами, розділеними пробілами, трохи більш зрозумілим використанням 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
це в основному класи, які ви можете створити за допомогою легкої скорочення. Ставтесь до них як до занять. Визначте їх на рівні модуля, щоб соління та інші користувачі могли їх знайти.
Робочий приклад на рівні глобального модуля:
>>> 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