how - namedtuple python 3 example
¿Qué son las “tuplas con nombre” en Python? (10)
Leyendo los cambios en Python 3.1 , encontré algo ... inesperado:
La tupla sys.version_info ahora es una tupla con nombre :
Nunca antes había oído hablar de tuplas con nombre, y pensé que los elementos podían indexarse por números (como en tuplas y listas) o por claves (como en dados). Nunca esperé que pudieran ser indexados en ambos sentidos.
Así, mis preguntas son:
- ¿Cómo se llaman las tuplas?
- ¿Cómo usarlos?
- ¿Por qué / cuándo debo usar tuplas con nombre en lugar de tuplas normales?
- ¿Por qué / cuándo debo usar tuplas normales en lugar de tuplas con nombre?
- ¿Hay algún tipo de "lista nombrada" (una versión mutable de la tupla nombrada)?
¿Cómo se llaman las tuplas?
Una tupla nombrada es una tupla.
Hace todo lo que una tupla puede.
Pero es más que solo una tupla.
Es una subclase específica de una tupla que se crea mediante programación según su especificación, con campos con nombre y una longitud fija.
Esto, por ejemplo, crea una subclase de tupla y, aparte de ser de longitud fija (en este caso, tres), se puede usar 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 instancia
>>> ant = ANamedTuple(1, ''bar'', [])
Podemos inspeccionarlo y utilizar 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 las tuplas nombradas, primero debe saber qué es una tupla. Una tupla es esencialmente una lista inmutable (no se puede cambiar in situ en la memoria).
Así es como puedes usar una tupla regular:
>>> 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 el 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 índice!
Haces un timbre llamado 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?
Puede hacer todo lo que pueden hacer las tuplas (ver más arriba), así como 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 una secuencia de comandos o programa grande, ¿dónde se suele definir una tupla con nombre?
Los tipos que creas con namedtuple
son básicamente clases que puedes crear con una taquigrafía sencilla. Trátalos como a las clases. Defínalos en el nivel del módulo, para que Pickle y otros usuarios puedan encontrarlos.
El ejemplo de trabajo, a 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 falta de búsqueda de 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 debo usar tuplas con nombre en lugar de tuplas normales?
Utilícelos cuando mejore su código para que la semántica de los elementos de la tupla se expresen en su código. Puede usarlos en lugar de un objeto si de otro modo usaría un objeto con atributos de datos invariables y sin funcionalidad. También puede subclasificarlos 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 debo 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 merece la legibilidad mejorada cuando se utiliza la tupla.
No hay memoria extra utilizada por las tuplas nombradas frente a las 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 de subclases que funcione como una tupla con nombre (y que de alguna manera bloquee la lista para que no 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
namedtuple
es una de las maneras más fáciles de limpiar su código y hacerlo más legible. Auto-documenta lo que está sucediendo en la tupla. Las instancias de Namedtuples son tan eficientes en memoria como las tuplas normales, 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á pasando en el primer ejemplo. Con un timbre nombrado, cada campo tiene un nombre. Y accedes a él por nombre en lugar de posición o índice. En lugar de p[1]
, podemos llamarlo psaturation. Es más fácil de entender. Y se ve más limpio.
Crear una instancia del grupo nombrado 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ías usar namedtuple?
- Como se acaba de decir, la pareja nombrada facilita mucho la comprensión de las tuplas. Por lo tanto, si necesita hacer referencia a los elementos en la tupla, entonces crearlos como timbres nombrados simplemente tiene sentido.
- Además de ser más ligero que un diccionario, nameduuple también mantiene el orden a diferencia del diccionario.
- Como en el ejemplo anterior, es más sencillo crear una instancia de se nombra un tupla que un diccionario. Y hacer referencia al elemento en la tupla con nombre se ve más limpio que un diccionario.
p.hue
lugar dep[''hue'']
.
La sintaxis
collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
- namedtuple está en la biblioteca de colecciones.
- typename: 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 cadenaxyz
(sin comas, solo espacios en blanco) ox, y, z
. - renombrar: si cambiar el nombre es
True
, los nombres de campo no válidos se reemplazan automáticamente con nombres posicionales. Por ejemplo,[''abc'', ''def'', ''ghi'',''abc'']
se convierte a[''abc'', ''_1'', ''ghi'', ''_3'']
, eliminando la palabra clave''def''
(ya 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 justo antes de construirse.
Aún puede acceder a timbres nombrados por su posición, si así lo desea. p[1] == p.saturation
. Todavía se desempaqueta como una tupla normal.
Métodos
Todos los métodos regulares de la tupla son compatibles. Ej .: min (), max (), len (), in, not in, concatenation (+), index, slice, etc. Y hay algunos adicionales para nameduuple. Nota: todos estos comienzan con un guión bajo. _replace
, _make
, _asdict
.
_replace
Devuelve una nueva instancia de la tupla con nombre 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)
Aviso : Los nombres de los campos no están entre comillas; son palabras clave aquí. Recuerde : las tuplas son inmutables, incluso si se nombran como tuplas y tienen el método _replace
. El _replace
produce una new
instancia; no modifica el original ni reemplaza el valor anterior. Por supuesto, puede guardar el nuevo resultado a la variable. p = p._replace(hue=169)
_make
Hace una nueva instancia 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 el iterable. Así que una lista o tupla dentro del paréntesis funciona, pero la secuencia de valores sin encerrar como un 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 una tupla llamada pero mutable https://pypi.python.org/pypi/namedlist
¿Cuál es el nombre de tu pareja?
Como su nombre lo indica, namedtuple es una tupla con nombre. En la tupla estándar, accedemos a los elementos utilizando el índice, mientras que nameduuple permite al usuario definir el nombre de los elementos. Esto es muy útil, especialmente para procesar archivos csv (valores separados por comas) y trabajar con conjuntos de datos grandes y complejos, donde el código se vuelve desordenado con el uso de índices (no tan pitón).
¿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)
En Python, en el interior 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 con nombre se aplicará directamente a la plantilla de clase predeterminada para generar una clase simple, este método permite que una gran cantidad de código mejore la legibilidad y también es muy conveniente al definir una clase.
Prueba esto:
collections.namedtuple()
Básicamente, los namedtuples
son fáciles de crear, tipos de objetos ligeros. Convierten las tuplas en contenedores convenientes para tareas simples. Con namedtuples
, no tiene que usar índices enteros para acceder a miembros de una tupla.
Ejemplos:
Código 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
Código 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
Todos los demás ya lo han respondido, pero creo que todavía tengo algo más que agregar.
Namedtuple podría considerarse intuitivamente como un atajo para definir una clase.
Ver una forma engorrosa y convencional para definir una 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''
En cuanto a 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''
las tuplas con nombre permiten la compatibilidad con el código que comprueba la versión como esta
>>> sys.version_info[0:2]
(3, 1)
mientras que permite que el código futuro sea más explícito usando esta sintaxis
>>> sys.version_info.major
3
>>> sys.version_info.minor
1
nameduuples son una gran característica, son un contenedor perfecto para datos. Cuando tenga que "almacenar" datos, usará 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 ligeras, pero carecen de legibilidad para un gran número de entradas en los campos de datos.
Los dos nombres son el compromiso perfecto para los dos enfoques, tienen una gran legibilidad, ligereza e inmutabilidad (además de que son polimórficos).
namedtuple es una función de fábrica para hacer una clase de tupla. Con esa clase podemos crear tuplas que también pueden llamarse por su 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)
Las tuplas con nombre son básicamente tipos de objetos livianos y fáciles de crear. Se puede hacer referencia a las instancias de tuplas con nombre mediante la eliminación de referencias a variables similares a objetos o la sintaxis de tuplas estándar. Se pueden usar de forma similar a la 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 su implementación en Python 2.4 .
Por ejemplo, es común representar un punto como una tupla (x, y)
. Esto conduce 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 con nombre aún son compatibles hacia atrás 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
Por lo tanto, debe usar tuplas con nombre en lugar de tuplas en cualquier lugar donde piense que la notación de objetos hará que su código sea más sintético y más legible . Personalmente, comencé a usarlos para representar tipos de valores muy simples, particularmente cuando los pasé como parámetros a funciones. Hace que las funciones sean más legibles, sin ver el contexto del empaque de la tupla.
Además, también puedes reemplazar clases ordinarias inmutables que no tienen funciones , solo campos con ellas. Incluso puedes usar tus tipos de tuplas con nombre como clases base:
class Point(namedtuple(''Point'', ''x y'')):
[...]
Sin embargo, al igual que con las tuplas, los atributos de las tuplas nombradas 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 útil para tipos de registro mutables que le permiten establecer nuevos valores en 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 con nombre" que le permita agregar nuevos campos. Es posible que desee utilizar un diccionario en esta situación. Las tuplas nombradas se pueden convertir a diccionarios usando pt1._asdict()
que devuelve {''x'': 1.0, ''y'': 5.0}
y se puede operar con todas las funciones habituales del diccionario.
Como ya se indicó, debe consultar la documentación para obtener más información a partir de la cual se construyeron estos ejemplos.