namedtuple — Subclase de tupla con campos con nombre¶
La tuple
estándar usa índices numéricos para acceder a sus miembros.
bob = ('Bob', 30, 'male')
print('Representation:', bob)
jane = ('Jane', 29, 'female')
print('\nField by index:', jane[0])
print('\nFields by index:')
for p in [bob, jane]:
print('{} is a {} year old {}'.format(*p))
Esto hace a las tuples
contenedores convenientes para usos simples.
$ python3 collections_tuple.py
Representation: ('Bob', 30, 'male')
Field by index: Jane
Fields by index:
Bob is a 30 year old male
Jane is a 29 year old female
Por el contrario, recordar qué índice se debe usar para cada valor puede
conducir a errores, especialmente si la tuple
tiene muchos campos y está#
construida lejos de donde se usa. Una namedtuple
asigna nombres, así como
el índice numérico, a cada miembro.
Definiendo¶
Las instancias namedtuple
son igual de eficientes en la memoria que las
tuplas normales porque no tienen diccionarios por instancia. Cada tipo de
namedtuple
está representada por su propia clase, que es creada usando la
función de fábrica namedtuple()
. Los argumentos son nombre de la nueva
clase y una cadena que contiene los nombres de los elementos.
import collections
Person = collections.namedtuple('Person', 'name age')
bob = Person(name='Bob', age=30)
print('\nRepresentation:', bob)
jane = Person(name='Jane', age=29)
print('\nField by name:', jane.name)
print('\nFields by index:')
for p in [bob, jane]:
print('{} is {} years old'.format(*p))
Como lo ilustra el ejemplo, es posible acceder a los campos de la
namedtuple
por nombre usando notación punteada (obj.attr
) así como
usando los índices posicionales de las tuplas estándar.
$ python3 collections_namedtuple_person.py
Representation: Person(name='Bob', age=30)
Field by name: Jane
Fields by index:
Bob is 30 years old
Jane is 29 years old
Al igual que una tuple
normal, una namedtuple
es inmutable. Esta
restricción permite que las instancias tuple
tengan un valor hash
consistente, que hace posible usarlos como claves en diccionarios y para ser
incluidos en conjuntos.
import collections
Person = collections.namedtuple('Person', 'name age')
pat = Person(name='Pat', age=12)
print('\nRepresentation:', pat)
pat.age = 21
Intentar cambiar un valor a través de su atributo nombrado da como resultado un
AttributeError
.
$ python3 collections_namedtuple_immutable.py
Representation: Person(name='Pat', age=12)
Traceback (most recent call last):
File "collections_namedtuple_immutable.py", line 17, in
<module>
pat.age = 21
AttributeError: can't set attribute
Nombre de campo inválidos¶
Los nombres de campo no son válidos si se repiten o entran en conflicto con palabras clave Python.
import collections
try:
collections.namedtuple('Person', 'name class age')
except ValueError as err:
print(err)
try:
collections.namedtuple('Person', 'name age age')
except ValueError as err:
print(err)
A medida que se analizan los nombres de los campos, los valores no válidos
causan excepciones ValueError
.
$ python3 collections_namedtuple_bad_fields.py
Type names and field names cannot be a keyword: 'class'
Encountered duplicate field name: 'age'
En situaciones donde se crea una namedtuple
basada en valores fuera del
control del programa (como para representar las filas devueltas por una
consulta de base de datos, donde el esquema no se conoce con atelación), la
opción rename
debe establecerse en True
así que los campos inválidos
son renombrados
import collections
with_class = collections.namedtuple(
'Person', 'name class age',
rename=True)
print(with_class._fields)
two_ages = collections.namedtuple(
'Person', 'name age age',
rename=True)
print(two_ages._fields)
Los nuevos nombres para campos renombrados dependen de su índice en la tupla,
entonces el campo con el nombre class
se convierte en _1
y el campo
duplicado age
se cambia a _2
.
$ python3 collections_namedtuple_rename.py
('name', '_1', 'age')
('name', 'age', '_2')
Atributos especiales¶
namedtuple
proporciona varios atributos y métodos útiles para trabajar con
subclases e instancias. Todos estas propiedades incorporadas tienen nombres
con un guion bajo (_
), que por convención en la mayoría de los programas de
Python indica un atributo privado. Para namedtuple
, sin embargo, el
prefijo está destinado a proteger el nombre de una colisión con nombres de
atributos proporcionados por el usuario.
Los nombres de los campos pasados a namedtuple
para definir la nueva clase
se guardan en el atributo _fields
.
import collections
Person = collections.namedtuple('Person', 'name age')
bob = Person(name='Bob', age=30)
print('Representation:', bob)
print('Fields:', bob._fields)
Aunque el argumento es una cadena única separada por espacios, el valor almacenado es la secuencia de nombres individuales.
$ python3 collections_namedtuple_fields.py
Representation: Person(name='Bob', age=30)
Fields: ('name', 'age')
Las instancias namedtuple
se pueden convertir a instancias OrderedDict
usando _asdict()
.
import collections
Person = collections.namedtuple('Person', 'name age')
bob = Person(name='Bob', age=30)
print('Representation:', bob)
print('As Dictionary:', bob._asdict())
Las llaves del OrderedDict
están en el mismo orden que los campos para la
namedtuple
.
$ python3 collections_namedtuple_asdict.py
Representation: Person(name='Bob', age=30)
As Dictionary: OrderedDict([('name', 'Bob'), ('age', 30)])
El método _replace()
crea una nueva instancia, reemplazando los valores de
algunos campos en el proceso.
import collections
Person = collections.namedtuple('Person', 'name age')
bob = Person(name='Bob', age=30)
print('\nBefore:', bob)
bob2 = bob._replace(name='Robert')
print('After:', bob2)
print('Same?:', bob is bob2)
Aunque el nombre implica que está modificando el objeto existente, porque las
namedtuple
son inmutables, el método en realidad devuelve un nuevo objeto.
$ python3 collections_namedtuple_replace.py
Before: Person(name='Bob', age=30)
After: Person(name='Robert', age=30)
Same?: False