Что такое «именованные кортежи» в Python? - PullRequest
798 голосов
/ 04 июня 2010

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

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

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

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

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

Ответы [ 11 ]

1053 голосов
/ 04 июня 2010

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

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

88 голосов
/ 04 декабря 2012

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)
67 голосов
/ 03 января 2016

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

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

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

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

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

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

>>> 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
38 голосов
/ 04 июня 2010

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

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

или:

user = ("John", 20)

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

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

28 голосов
/ 04 июня 2010

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

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

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

>>> sys.version_info.major
3
>>> sys.version_info.minor
1
9 голосов
/ 16 октября 2016

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. Это легче понять. И выглядит чище.

Создать экземпляр именованного кортежа проще, чем создать словарь.

# 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. Как только что сказано, именованный кортеж делает понимание кортежей очень Полегче. Так что если вам нужно ссылаться на элементы в кортеже, то создание их как именованных кортежей имеет смысл.
  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)

* +1077 * * Пример 1 078 *

>>>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()
* ** 1 124 Пример ** 1127 тысяча сто двадцать шесть * >>>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
8 голосов
/ 27 ноября 2015

Что такое namedtle?

Как следует из названия, 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 голосов
/ 19 июля 2013

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

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

1 голос
/ 28 февраля 2018

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

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
0 голосов
/ 01 апреля 2019

Другим способом (новым способом) использования именованного кортежа является использование 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)
...