Что такое "названные кортежи" в Python?
Чтение изменений в Python 3.1, я нашел что-то... неожиданное:
Кортеж sys.version_info теперь является с именем tuple:
Я никогда не слышал об именованных кортежах раньше, и я думал, что элементы могут быть проиндексированы цифрами (например, в кортежах и списках) или ключами (например, в dicts). Я никогда не ожидал, что они могут быть проиндексированы в обоих направлениях.
Таким образом, мои вопросы:
- Как называются кортежи?
- Как их использовать?
- Почему/когда я должен использовать именованные кортежи вместо обычных кортежей?
- Почему/когда я должен использовать обычные кортежи вместо названных кортежей?
- Есть ли какой-либо "именованный список" (измененная версия именованного кортежа)?
Ответы
Ответ 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
Я не знаю какой-либо формы "named list", которая позволяет добавлять новые поля. Вы можете просто использовать словарь в этой ситуации. Именованные кортежи могут быть преобразованы в словари, используя pt1._asdict()
, который возвращает {'x': 1.0, 'y': 5.0}
и может работать со всеми обычными функциями словаря.
Как уже отмечалось, вы должны проверить документацию для получения дополнительной информации, из которой были построены эти примеры.
Ответ 2
namedtuple - это factory функция для создания класса кортежа. С этим классом мы можем создавать кортежи, которые также вызываются по имени.
import collections
#Create a namedtuple class with names "a" "b" "c"
Row = collections.namedtuple("Row", ["a", "b", "c"], verbose=False, rename=False)
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)
Ответ 3
namedtuples - отличная функция, они идеальный контейнер для данных. Когда вам нужно "хранить" данные, вы должны использовать кортежи или словари, например:
user = dict(name="John", age=20)
или
user = ("John", 20)
Словарный подход является ошеломляющим, так как dict изменен и медленнее, чем кортежи. С другой стороны, кортежи являются неизменными и легкими, но не имеют удобочитаемости для большого количества записей в полях данных.
namedtuples являются идеальным компромиссом для двух подходов, имеют большую читаемость, легковесность и неизменность (плюс они являются полиморфными!).
Ответ 4
Как называются кортежи?
Чтобы понять именованные кортежи, вам сначала нужно знать, что такое кортеж. Кортеж по существу является неизменным (не может быть изменен на месте в памяти).
Здесь вы можете использовать регулярный кортеж:
>>> 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')
Почему/когда я должен использовать именованные кортежи вместо обычных кортежей?
Используйте их, когда он улучшает ваш код, чтобы иметь семантику элементов кортежа, выраженных в коде. Вы можете использовать их вместо объекта, если в противном случае вы использовали бы объект с неизменными атрибутами данных и без каких-либо функций. Вы также можете подклассировать их, чтобы добавить функциональность, например:
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
Ответ 5
названные кортежи допускают обратную совместимость с кодом, который проверяет версию, подобную этой
>>> sys.version_info[0:2]
(3, 1)
позволяя будущему коду быть более явным, используя этот синтаксис
>>> sys.version_info.major
3
>>> sys.version_info.minor
1
Ответ 6
- один из самых простых способов очистки кода и делает его более читаемым. Он сам документирует то, что происходит в кортеже. Экземпляры Namedtuples столь же эффективны, как и регулярные кортежи, поскольку у них нет словарей для каждого экземпляра, что делает их быстрее, чем словари.
from collections import namedtuple
Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
p = Color(170, 0.1, 0.6)
if p.saturation >= 0.5:
print "Whew, that is bright!"
if p.luminosity >= 0.5:
print "Wow, that is light"
Без обозначения каждого элемента в кортеже он будет выглядеть следующим образом:
p = (170, 0.1, 0.6)
if p[1] >= 0.5:
print "Whew, that is bright!"
if p[2]>= 0.5:
print "Wow, that is light"
Намного сложнее понять, что происходит в первом примере. С namedtuple каждое поле имеет имя. И вы получаете доступ к нему по имени, а не по позиции или индексу. Вместо p[1]
мы можем назвать это p.saturation. Это легче понять. И он выглядит чище.
Создание экземпляра namedtuple проще, чем создание словаря.
# dictionary
>>>p = dict(hue = 170, saturation = 0.1, luminosity = 0.6)
>>>p['hue']
170
#nametuple
>>>from collections import namedtuple
>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)
>>>p.hue
170
Когда вы можете использовать namedtuple
- Как только что указано, именованный элемент значительно улучшает понимание кортежей
Полегче. Поэтому, если вам нужно ссылаться на элементы в кортеже, тогда
создавая их как namedtuples, имеет смысл.
- Помимо более легкого, чем словарь, namedtuple также
сохраняет порядок в отличие от словаря.
- Как и в примере выше, проще создать экземпляр
namedtuple, чем словарь. И ссылка на элемент в названии
Кортеж выглядит чище, чем словарь.
p.hue
, а не
p['hue']
.
Синтаксис
collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
- namedtuple находится в библиотеке коллекций.
- typename: Это имя нового подкласса кортежа.
- field_names: последовательность имен для каждого поля. Это может быть последовательность
как в списке
['x', 'y', 'z']
или строке x y z
(без запятых, просто
пробел) или x, y, z
.
- rename: Если переименовать
True
, недопустимые имена полей автоматически
заменены позиционными именами. Например, ['abc', 'def', 'ghi','abc']
преобразуется в ['abc', '_1', 'ghi', '_3']
, устраняя
ключевое слово 'def'
(так как это зарезервированное слово для определения функций)
и повторяющееся имя поля 'abc'
.
- verbose: если verbose является
True
, определение класса печатается только
до того, как он будет построен.
Вы можете по-прежнему обращаться к namedtuples по своей позиции, если вы этого захотите. p[1] == p.saturation
. Он по-прежнему распаковывается, как обычный кортеж.
Методы
Поддерживаются все обычные кортежи. Ex: min(), max(), len(), in, not in, concatenation (+), index, slice и т.д. И есть несколько дополнительных для namedtuple. Примечание: все они начинаются с подчеркивания. _replace
, _make
, _asdict
.
_replace
Возвращает новый экземпляр именованного кортежа, который заменяет указанные поля новыми значениями.
Синтаксис
somenamedtuple._replace(kwargs)
Пример
>>>from collections import namedtuple
>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)
>>>p._replace(hue=87)
Color(87, 0.1, 0.6)
>>>p._replace(hue=87, saturation=0.2)
Color(87, 0.2, 0.6)
Примечание: имена полей не указаны в кавычках; они являются ключевыми словами здесь.
Запомнить. Кортежи неизменяемы - даже если они именованы и имеют метод _replace
. _replace
создает экземпляр new
; он не изменяет оригинал или не заменяет старое значение. Конечно, вы можете сохранить новый результат в переменной. p = p._replace(hue=169)
_make
Создает новый экземпляр из существующей последовательности или iterable.
Синтаксис
somenamedtuple._make(iterable)
Пример
>>>data = (170, 0.1, 0.6)
>>>Color._make(data)
Color(hue=170, saturation=0.1, luminosity=0.6)
>>>Color._make([170, 0.1, 0.6]) #the list is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)
>>>Color._make((170, 0.1, 0.6)) #the tuple is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)
>>>Color._make(170, 0.1, 0.6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 15, in _make
TypeError: 'float' object is not callable
Что случилось с последним? Элемент внутри скобки должен быть итерируемым. Таким образом, список или кортеж внутри скобок работает, но последовательность значений без включения в качестве итерации возвращает ошибку.
_asdict
Возвращает новый OrderedDict, который сопоставляет имена полей с их соответствующими значениями.
Синтаксис
somenamedtuple._asdict()
Пример
>>>p._asdict()
OrderedDict([('hue', 169), ('saturation', 0.1), ('luminosity', 0.6)])
Ссылка: https://www.reddit.com/r/Python/comments/38ee9d/intro_to_namedtuple/
Существует также именованный список, который похож на именованный кортеж, но изменяемый
https://pypi.python.org/pypi/namedlist
Ответ 7
Что такое namedtuple?
Как следует из названия, namedtuple является кортежем с именем. В стандартном кортеже мы обращаемся к элементам с помощью индекса, тогда как namedtuple позволяет пользователю определять имя для элементов. Это очень удобно, особенно обрабатывая файлы csv (значения, разделенные запятой) и работая со сложным и большим набором данных, где код становится беспорядочным с использованием индексов (не так pythonic).
Как их использовать?
>>>from collections import namedtuple
>>>saleRecord = namedtuple('saleRecord','shopId saleDate salesAmout totalCustomers')
>>>
>>>
>>>#Assign values to a named tuple
>>>shop11=saleRecord(11,'2015-01-01',2300,150)
>>>shop12=saleRecord(shopId=22,saleDate="2015-01-01",saleAmout=1512,totalCustomers=125)
Чтение
>>>#Reading as a namedtuple
>>>print("Shop Id =",shop12.shopId)
12
>>>print("Sale Date=",shop12.saleDate)
2015-01-01
>>>print("Sales Amount =",shop12.salesAmount)
1512
>>>print("Total Customers =",shop12.totalCustomers)
125
Интересный сценарий обработки CSV:
from csv import reader
from collections import namedtuple
saleRecord = namedtuple('saleRecord','shopId saleDate totalSales totalCustomers')
fileHandle = open("salesRecord.csv","r")
csvFieldsList=csv.reader(fileHandle)
for fieldsList in csvFieldsList:
shopRec = saleRecord._make(fieldsList)
overAllSales += shopRec.totalSales;
print("Total Sales of The Retail Chain =",overAllSales)
Ответ 8
В Python внутри есть хорошее использование контейнера, называемого именованным кортежем, его можно использовать для создания определения класса и имеет все функции исходного кортежа.
Использование named tuple будет напрямую применяться к шаблону класса по умолчанию для создания простого класса, этот метод позволяет значительно улучшить код для чтения, а также очень удобно при определении класса.