Pregunta ¿Qué son las "tuplas con nombre" en Python?


Leer el cambios en Python 3.1, Encontré algo ... inesperado:

La tupla sys.version_info es ahora una tupla llamada:

Nunca escuché acerca de las tuplas nombradas antes, y pensé que los elementos podrían estar indexados por números (como en tuplas y listas) o por claves (como en los dicts). Nunca esperé que pudieran indexarse ​​en ambos sentidos.

Por lo tanto, mis preguntas son:

  • ¿Qué se llaman tuplas?
  • Cómo usarlos?
  • ¿Por qué / cuándo debería usar tuplas con nombre en lugar de tuplas normales?
  • ¿Por qué / cuándo debería usar tuplas normales en lugar de tuplas con nombre?
  • ¿Hay algún tipo de "lista nombrada" (una versión mutable de la tupla nombrada)?

671
2018-06-03 23:50


origen


Respuestas:


Las tuplas con nombre son básicamente tipos de objetos ligeros y fáciles de crear. Las instancias de tuplas nombradas se pueden referenciar usando la desreferenciación de variables similar a un objeto o la sintaxis de tupla estándar. Se pueden usar de manera similar a struct u otros tipos de registros comunes, excepto que son inmutables. Se agregaron en Python 2.6 y Python 3.0, aunque hay una receta para la implementación en Python 2.4.

Por ejemplo, es común representar un punto como una tupla (x, y). Esto lleva a un código como el siguiente:

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)

Usando una tupla nombrada se vuelve más legible:

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)

Sin embargo, las tuplas nombradas todavía son compatibles con las tuplas normales, por lo que lo siguiente seguirá funcionando:

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

Así, deberías usar tuplas con nombre en lugar de tuplas donde creas que la notación de objetos hará que tu código sea más pitónico y más fácil de leer. Personalmente, he comenzado a usarlos para representar tipos de valores muy simples, particularmente cuando los paso como parámetros a las funciones. Hace que las funciones sean más legibles, sin ver el contexto del embalaje de la tupla.

Además, también puede reemplazar ordinario inmutable clases que no tienen funciones, solo campos con ellos. Incluso puede usar sus tipos de tuplas nombradas como clases base:

class Point(namedtuple('Point', 'x y')):
    [...]

Sin embargo, como con las tuplas, los atributos en tuplas con nombre son inmutables:

>>> Point = namedtuple('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
AttributeError: can't set attribute

Si desea poder cambiar los valores, necesita otro tipo. Hay una receta práctica para tipos de registro mutables que le permiten establecer nuevos valores a los atributos.

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

Sin embargo, no conozco ninguna forma de "lista de nombres" que le permita agregar nuevos campos. Quizás solo quiera usar un diccionario en esta situación. Las tuplas con nombre se pueden convertir a diccionarios usando pt1._asdict() que devuelve {'x': 1.0, 'y': 5.0} y puede ser operado con todas las funciones usuales del diccionario.

Como ya se señaló, debes revisa la documentación para obtener más información de la que se construyeron estos ejemplos.


900
2018-06-04 00:19



namedtuple es un función de fábrica para hacer una clase de tupla Con esa clase podemos crear tuplas que también se pueden llamar por nombre.

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)

76
2017-12-04 10:30



¿Qué se llaman tuplas?

Una tupla nombrada es una tupla.

Hace todo lo que una tupla puede hacer.

Pero es más que solo una tupla.

Es una subclase específica de una tupla que se crea programáticamente según su especificación, con campos con nombre y una longitud fija.

Esto, por ejemplo, crea una subclase de tupla, y además de ser de longitud fija (en este caso, tres), puede usarse en cualquier lugar donde se use una tupla sin romperse. Esto se conoce como sustituibilidad de Liskov:

>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)

Esto lo ejemplifica:

>>> ant = ANamedTuple(1, 'bar', [])

Podemos inspeccionarlo y usar sus atributos:

>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']

Explicación más profunda

Para entender tuplas con nombre, primero necesita saber qué es una tupla. Una tupla es esencialmente una lista inmutable (no se puede cambiar in situ en la memoria).

A continuación, le mostramos cómo puede usar una tupla común:

>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'

Puede expandir una tupla con desempaquetado iterable:

>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'

Las tuplas con nombre son tuplas que permiten acceder a sus elementos por nombre en lugar de solo por índice.

Haces una ruta de nombre así:

>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])

También puede usar una sola cadena con los nombres separados por espacios, un uso ligeramente más legible de la API:

>>> Student = namedtuple('Student', 'first last grade')

Cómo usarlos?

Puedes hacer todo lo que las tuplas pueden hacer (ver arriba) y hacer lo siguiente:

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

Un comentarista preguntó:

En un script o programa grande, ¿dónde se suele definir una tupla nombrada?

Los tipos que creas con namedtuple son básicamente clases que puedes crear con fácil taquigrafía. Trátelos como clases. Defínalos en el nivel de módulo, para que pickle y otros usuarios puedan encontrarlos.

El ejemplo de trabajo, en el nivel de módulo global:

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

Y esto demuestra la falla en buscar la definición:

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

¿Por qué / cuándo debería usar tuplas con nombre en lugar de tuplas normales?

Úselos cuando mejore su código para tener la semántica de los elementos de tupla expresados ​​en su código. Puede usarlos en lugar de un objeto si, de lo contrario, usaría un objeto con atributos de datos invariables y sin funcionalidad. Tú también puedes subclases para agregar funcionalidad, por ejemplo:

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)

¿Por qué / cuándo debería usar tuplas normales en lugar de tuplas con nombre?

Probablemente sería una regresión cambiar de usar tuplas con nombre a tuplas. La decisión de diseño inicial se centra en si el costo del código adicional involucrado vale la pena la legibilidad mejorada cuando se utiliza la tupla.

No hay memoria extra utilizada por tuplas con nombre frente a tuplas.

¿Hay algún tipo de "lista nombrada" (una versión mutable de la tupla nombrada)?

Está buscando un objeto ranurado que implemente toda la funcionalidad de una lista de tamaño estático o una lista subclase que funcione como una tupla nombrada (y que de alguna manera impida que la lista cambie de tamaño).

Un ejemplo ahora expandido, y quizás incluso sustituible por Liskov, del primero:

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

Y para usar, solo subclase y defina __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

43
2018-01-03 04:54



namedtuples son una gran característica, son un contenedor perfecto para los datos. Cuando tenga que "almacenar" datos, usaría tuplas o diccionarios, como:

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

o:

user = ("John", 20)

El enfoque del diccionario es abrumador, ya que los dict son mutables y más lentos que las tuplas. Por otro lado, las tuplas son inmutables y livianas, pero carecen de legibilidad para una gran cantidad de entradas en los campos de datos.

namedtuples son el compromiso perfecto para los dos enfoques, tienen gran legibilidad, ligereza e inmutabilidad (¡además son polimórficos!).


36
2018-06-04 09:32



tuplas nombradas permiten compatibilidad con versiones anteriores con código que busca la versión como esta

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

al tiempo que permite que el código futuro sea más explícito al usar esta sintaxis

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

28
2018-06-04 00:12



namedtuple

es una de las formas más fáciles de limpiar su código y hacerlo más legible. Autoe documenta lo que está sucediendo en la tupla. Las instancias de Namedtuples son tan eficientes en memoria como las tuplas regulares, ya que no tienen diccionarios por instancia, lo que las hace más rápidas que los diccionarios.

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"

Sin nombrar cada elemento en la tupla, se leería así:

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"

Es mucho más difícil entender lo que está sucediendo en el primer ejemplo. Con una tilde nombrada, cada campo tiene un nombre. Y accede a él por su nombre en lugar de posición o índice. En lugar de p[1], podemos llamarlo p.saturation. Es más fácil de entender Y se ve más limpio.

Crear una instancia de namedtuple es más fácil que crear un diccionario.

# 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

¿Cuándo podría usar namedtuple?

  1. Como acabo de decir, la tilde nombrada hace entender mucho a las tuplas más fácil. Entonces, si necesita referenciar los elementos en la tupla, entonces crearlos como namedtuples simplemente tiene sentido.
  2. Además de ser más ligero que un diccionario, namedtuple también mantiene el orden a diferencia del diccionario.
  3. Como en el ejemplo anterior, es más simple crear una instancia de namedtuple que el diccionario. Y haciendo referencia al elemento en el nombrado tupla se ve más limpio que un diccionario. p.hue más bien que p['hue'].

La sintaxis

collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
  • namedtuple está en la biblioteca de colecciones.
  • nombre de tipo: este es el nombre de la nueva subclase de tupla.
  • field_names: una secuencia de nombres para cada campo. Puede ser una secuencia como en una lista ['x', 'y', 'z'] o cadena x y z (sin comas, solo espacio en blanco) o x, y, z.
  • renombrar: si renombrar es True, los nombres de campo inválidos son automáticamente reemplazado con nombres posicionales. Por ejemplo, ['abc', 'def', 'ghi','abc'] se convierte a ['abc', '_1', 'ghi', '_3'], eliminando el palabra clave 'def' (dado que es una palabra reservada para definir funciones) y el nombre de campo duplicado 'abc'.
  • verbose: si verbose es True, la definición de la clase se imprime solo antes de ser construido.

Todavía puede acceder a namedtuples por su posición, si así lo desea. p[1] == p.saturation. Todavía desempaqueta como una tupla regular.

Métodos

Todos métodos regulares de tupla son compatibles. Ej: min (), max (), len (), in, not in, concatenación (+), index, slice, etc. Y hay algunos adicionales para namedtuple. Nota: todos comienzan con un guion bajo. _replace, _make, _asdict.

_replace Devuelve una nueva instancia de la tupla nombrada que reemplaza los campos especificados con nuevos valores.

La sintaxis

somenamedtuple._replace(kwargs)

Ejemplo

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

darse cuenta: Los nombres de los campos no están entre comillas; son palabras clave aquí. Recuerda: Las tuplas son inmutables, incluso si se nombran como títeres y tienen el _replace método. los _replace produce un new ejemplo; no modifica el original ni reemplaza el valor anterior. Por supuesto, puedes guardar el nuevo resultado en la variable. p = p._replace(hue=169)

_make

Crea una instancia nueva a partir de una secuencia existente o iterable.

La sintaxis

somenamedtuple._make(iterable)

Ejemplo

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

¿Qué pasó con el último? El elemento dentro del paréntesis debe ser iterable. Entonces, una lista o tupla dentro del paréntesis funciona, pero la secuencia de valores sin incluir como iterable devuelve un error.

_asdict

Devuelve un nuevo OrderedDict que asigna nombres de campo a sus valores correspondientes.

La sintaxis

somenamedtuple._asdict()

Ejemplo

 >>>p._asdict()
OrderedDict([('hue', 169), ('saturation', 0.1), ('luminosity', 0.6)])

Referencia: https://www.reddit.com/r/Python/comments/38ee9d/intro_to_namedtuple/

También hay una lista de nombres que es similar a la tupla llamada pero mutable https://pypi.python.org/pypi/namedlist


9
2017-10-16 04:22



¿Qué es namedtuple?

Como su nombre lo sugiere, namedtuple es una tupla con nombre. En una tupla estándar, accedemos a los elementos usando el índice, mientras que namedtuple permite al usuario definir el nombre de los elementos. Esto es muy útil especialmente al procesar archivos csv (valores separados por comas) y trabajar con conjuntos de datos complejos y grandes, donde el código se vuelve complicado con el uso de índices (no tan pitónicos).

Cómo usarlos?

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

Leyendo

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

Escenario interesante en el procesamiento de 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)

6
2017-11-27 07:48



En el interior de Python hay un buen uso del contenedor llamado tupla con nombre, se puede usar para crear una definición de clase y tiene todas las características de la tupla original.

El uso de la tupla nombrada se aplicará directamente a la plantilla de clase predeterminada para generar una clase simple, este método permite una gran cantidad de código para mejorar la legibilidad y también es muy conveniente al definir una clase.


5
2017-07-19 03:34