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


907

Читая изменения в Python 3.1 , я обнаружил кое-что ... неожиданное:

Кортеж sys.version_info теперь является именованным кортежем :

Я никогда не слышал об именованных кортежах раньше, и я думал, что элементы могут быть проиндексированы либо по номерам (как в кортежах и списках), либо по ключам (как в диктах). Я никогда не ожидал, что они могут быть проиндексированы в обоих направлениях.

Итак, мои вопросы:

  • Как называются кортежи?
  • Как их использовать?
  • Почему / когда я должен использовать именованные кортежи вместо обычных кортежей?
  • Почему / когда я должен использовать нормальные кортежи вместо именованных?
  • Есть ли какой-либо «именованный список» (изменяемая версия именованного кортежа)?

Ответы:


1196

Именованные кортежи - это, в основном, простые в создании, легкие типы объектов. На именованные экземпляры кортежей можно ссылаться, используя объектную переменную разыменования или стандартный синтаксис кортежей. Они могут использоваться аналогично 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}и могут работать со всеми обычными функциями словаря.

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


35
из python 3.7, также рассмотрите классы данных как альтернативу (бэкпорт доступен для 3.6, но не более ранних версий)
innov8

3
Если вам нужен изменяемый тип записи - используйте простой класс с определенным__slots__
madzohan

что является основной причиной использования rcdtype, а не классов данных
Voyager

Альтернативой dict является словарь атрибутов stackoverflow.com/questions/4984647/…
mrgloom

Поскольку это ответ, который вы всегда найдете, возможно, стоит упомянуть, что теперь есть также, typing.NamedTupleкоторый учитывает подсказки типов и особенно удобен для создания подклассов.
DerWeh

101

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

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)

5
Параметры verbose и rename по умолчанию имеют значение False, поэтому их не нужно явно устанавливать на это значение.
Трисмегистос

namedtuple is a factory function for making a tuple class.это, вероятно, единственный правильный ответ здесь: P
Mr_and_Mrs_D

90

Как называются кортежи?

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

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

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

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

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

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

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

44

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

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

или:

user = ("John", 20)

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

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


9
Имейте в виду, что namedtles намного медленнее, чем dicts, если вы обращаетесь к их атрибутам по имени: ntuple.foovs ntuple[1]последний намного быстрее. Подробнее об этом: stackoverflow.com/questions/2646157/…
Rotareti

28

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

>>> sys.version_info[0:2]
(3, 1)

позволяя будущему коду быть более явным с помощью этого синтаксиса

>>> sys.version_info.major
3
>>> sys.version_info.minor
1

12

namedtuple

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

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"

Намного сложнее понять, что происходит в первом примере. С именованным кортежем каждое поле имеет имя. И вы получаете доступ к нему по имени, а не по позиции или индексу. Вместо этого 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

  1. Как уже говорилось, namedtup значительно упрощает понимание кортежей. Так что если вам нужно сослаться на элементы в кортеже, то создание их как именованных кортежей имеет смысл.
  2. Помимо того, что namedtuple более легкий, чем словарь, он также сохраняет порядок в отличие от словаря.
  3. Как и в примере выше, создать экземпляр 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.
  • переименовать: если переименовать True, недопустимые имена полей автоматически заменяются позиционными именами. Например, ['abc', 'def', 'ghi','abc']преобразуется в ['abc', '_1', 'ghi', '_3'], исключая ключевое слово 'def'(так как это зарезервированное слово для определения функций) и дублирующее имя поля 'abc'.
  • verbose: если verbose True, определение класса печатается непосредственно перед сборкой.

Вы по-прежнему можете получить доступ к именованным кортежам по их позиции, если вы того пожелаете. p[1] == p.saturation, Он все еще распаковывается как обычный кортеж.

методы

Все обычные методы кортежа поддерживаются. Пример: min (), max (), len (), in, not in, конкатенация (+), 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

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

Синтаксис

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


Тем не менее, обратите внимание, что согласно PEP8 отдельный знак подчеркивания считается «слабым индикатором« внутреннего использования »» со своим собственным поведением. Будьте осторожны при использовании функций, которые начинаются с _!
Дженс

8

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

Как следует из названия, namedtuple - это кортеж с именем. В стандартном кортеже мы получаем доступ к элементам, используя индекс, тогда как namedtuple позволяет пользователю определять имя для элементов. Это очень удобно, особенно при обработке файлов csv (с разделенными запятыми значениями) и работе со сложным и большим набором данных, когда код становится беспорядочным с использованием индексов (не так уж пифонических).

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

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

5

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

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


2

Другой способ (новый способ) использования именованного кортежа - это использование NamedTuple из набора текста: подсказки типа в namedtuple

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

(1) Перед использованием именованного кортежа код выглядит так:

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)
print(line_length)

(2) Теперь мы используем именованный кортеж

from typing import NamedTuple, Number

наследовать класс NamedTuple и определить имя переменной в новом классе. test - это название класса

class test(NamedTuple):
x: Number
y: Number

создавать экземпляры из класса и присваивать им значения

pt1 = test(1.0, 5.0)   # x is 1.0, and y is 5.0. The order matters
pt2 = test(2.5, 1.5)

использовать переменные из экземпляров для расчета

line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)
print(line_length)

1

Попробуй это:

collections.namedtuple()

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

Примеры:

Код 1:

>>> from collections import namedtuple

>>> Point = namedtuple('Point','x,y')

>>> pt1 = Point(1,2)

>>> pt2 = Point(3,4)

>>> dot_product = ( pt1.x * pt2.x ) +( pt1.y * pt2.y )

>>> print dot_product
11

Код 2:

>>> from collections import namedtuple

>>> Car = namedtuple('Car','Price Mileage Colour Class')

>>> xyz = Car(Price = 100000, Mileage = 30, Colour = 'Cyan', Class = 'Y')

>>> print xyz

Car(Price=100000, Mileage=30, Colour='Cyan', Class='Y')
>>> print xyz.Class
Y

-1

Все остальные уже ответили, но я думаю, что мне еще есть что добавить.

Namedtuple может быть интуитивно понят как ярлык для определения класса.

Смотрите громоздкий и традиционный способ определения class.

class Duck:
    def __init__(self, color, weight):
        self.color = color
        self.weight = weight
red_duck = Duck('red', '10')

    In [50]: red_duck
    Out[50]: <__main__.Duck at 0x1068e4e10>
    In [51]: red_duck.color
    Out[51]: 'red'

Что касается namedtuple

from collections import namedtuple
Duck = namedtuple('Duck', ['color', 'weight'])
red_duck = Duck('red', '10')

In [54]: red_duck
Out[54]: Duck(color='red', weight='10')
In [55]: red_duck.color
Out[55]: 'red'

2
Извините, но это неправильно. Именованный кортеж также поддерживает эти: red_duck[0]или len(red_duck)или for x in red_duck: print(x). Кроме того, именованные кортежи являются неизменяемыми, поэтому эти операции завершатся с ошибкой: red_duck[0] = 2, red_duck.foo = 'bar'. Поскольку они являются неизменяемыми, именованные кортежи могут использоваться в качестве dictключей.
Денилсон Са

Да, это основы.
Исчисление

1
@JawSaw Нет, это не "основы". Именованные кортежи поддерживают совершенно другой набор функций, чем обычные классы. Хотя по сути именованные кортежи являются классами, это не означает, что классы называются кортежами.
connectyourcharger
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.