
Colecciones en Python: namedtuple()
Tabla de Contenido
Note
En este artículo, conocerás la función namedtuple
del módulo collections
de la biblioteca estándar de Python.
¿Qué es namedtuple()
?
namedtuple()
es una función que retorna una nueva clase, la cual permite acceder a los valores de una tupla tanto por índice como a través de un nombre definido al crear la tupla.
Primero, examinaremos la firma de la función para entender cómo funciona y, posteriormente, analizaremos por qué y cuándo es beneficioso usarla.
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None):
Esta firma revela cinco argumentos importantes:
typename
: el nombre que se asignará a la nueva clase creada.field_names
: ya sea un string o un iterable de strings que representan los nombres de los campos de la tupla. El orden de estos strings determina cómo se accederán los valores.rename
: un valor booleano que, si se activa, permite que la función reemplace nombres duplicados o conflictivos automáticamente.defaults
: permite definir valores predeterminados para los campos de la tupla, asignando valores desde el final hacia el inicio de la lista de campos.module
: un string que asocia la nueva tupla a un módulo específico.
Note
Los argumentos definidos después de *
son argumentos que deben pasarse como keyword arguments
(es decir, como nombre=valor
), no de manera posicional.
Ejemplos de uso
Crear una namedtuple
Primero, veamos cómo crear una nueva tupla sencilla:
from collections import namedtuple
Coordinates = namedtuple("Coordinates", ["x", "y", "z"])
point_1 = Coordinates(1, 2, 3)
print(point_1)
print('x:', point_1.x, 'y:', point_1.y, 'z:', point_1.z)
print('x:', point_1[0], 'y:', point_1[1], 'z:', point_1[2])
El resultado de este código será:
$ python main.py
Coordinates(x=1, y=2, z=3)
x: 1 y: 2 z: 3
x: 1 y: 2 z: 3
Como observamos, creamos una nueva clase llamada Coordinates
con campos x
, y
, y z
. Luego instanciamos point_1
y accedimos a sus valores tanto por nombre como por índice.
Note
Al declarar point_1
, los valores se asignan en el mismo orden en que se especifican en la tupla: primero x
, después y
, y finalmente z
.
Crear una namedtuple
con valores por defecto
Definiendo valores por defecto, que deben ser iterables, podemos especificar los valores que se asignarán a los campos si no se proporcionan al crear la instancia:
from collections import namedtuple
UserData = namedtuple(
"UserData",
["name", "age", "email", "country"],
defaults=("example@mail.com", "CO")
)
david = UserData( "David", 28, )
juan = UserData("Juan", 24, "juan@mail.com")
print(david)
print(juan)
El resultado de este código será:
UserData(name='David', age=28, email='example@mail.com', country='CO')
UserData(name='Juan', age=24, email='juan@mail.com', country='CO')
Los valores por defecto se asignan de derecha a izquierda a los campos que no tienen valores asignados al crear la instancia.
Pero debemos de tener en cuenta que si no pasamos todos los argumentos que no están definidos por defecto, la función lanzará un error.
unknown = UserData()
print(unknown)
Traceback (most recent call last):
File "/home/dacacode/dacadev/personal-blog/tempCodeRunnerFile.python", line 9, in <module>
unknown = UserData()
^^^^^^^^^^
TypeError: UserData.__new__() missing 2 required positional arguments: 'name' and 'age'
Controlar el renombramiento de los campos
Activando el argumento rename
como True
, la función automáticamente renombra campos duplicados o que sean palabras reservadas del lenguaje. Primero vamos a crear una tupla normal con nombres de campos que pueden entrar en conflicto.
from collections import namedtuple
dynamic_fields = ['name', 'def', 'arg', 'name']
EspecialInfo = namedtuple("EspecialInfo", dynamic_fields)
print(EspecialInfo)
Al ejecutar el código vas a obtener un error como el siguiente:
Traceback (most recent call last):
File "/home/dacacode/dacadev/personal-blog/tempCodeRunnerFile.python", line 4, in <module>
EspecialInfo = namedtuple("EspecialInfo", dynamic_fields)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dacacode/.pyenv/versions/3.11.5/lib/python3.11/collections/__init__.py", line 398, in namedtuple
raise ValueError('Type names and field names cannot be a '..)
ValueError: Type names and field names cannot be a keyword: 'def'
Pero si habilitamos la opcion de rename
el código funcionará, ya que la función se encarga de procesar esos casos.
from collections import namedtuple
dynamic_fields = ['name', 'def', 'arg', 'name']
EspecialInfoV2 = namedtuple("EspecialInfoV2", dynamic_fields, rename=True)
print(EspecialInfoV2)
data = EspecialInfoV2('David', 'print', 'Hello, World!', 'David')
print(data)
Ahora si obtendremos un resultado positivo:
<class '__main__.EspecialInfoV2'>
EspecialInfoV2(name='David', _1='print', arg='Hello, World!', _3='David')
En donde podemos ver que los atributos que son keywords
y duplicados
han sido renombrados por valores posicionales, en este caso _1
y _3
.
Las namedtuples
son Clases
Por último, pero no menos importante, recuerda que el resultado de la función namedtuple
es una clase que hereda de tuple
, lo que significa que podemos usarla como cualquier otra clase. Por ejemplo, podemos usar la nueva clase como padre de otra para extender su funcionalidad:
from collections import namedtuple
Coordinates = namedtuple("Coordinates", ["x", "y", "z"])
class Point(Coordinates):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y, self.z + other.z)
point_1 = Point(1, 2, 3)
point_2 = Point(4, 5, 6)
print(point_1)
print(point_2)
print(point_1 + point_2)
El resultado de este código será:
Point(x=1, y=2, z=3)
Point(x=4, y=5, z=6)
Point(x=5, y=7, z=9)
¿Por qué usar namedtuples
?
Una de las principales razones para usar las namedtuple
es la mejora de la legibilidad del código, ya que al momento los atributos para acceder a sus elementos es más fácil de entender que si lo hacemos por índice.
from collections import namedtuple
UserInfo = namedtuple('UserInfo', ['name', 'age', 'email'])
# Mejora la legibilidad
user = UserInfo('David', 28, 'mail@mail.com')
user.name # David
user.age # 30
user.email # mail@mail.com
# Debes tener en tu mente el orden de los campos, no es lo ideal!
userRaw = {'David', 28, 'mail@mail.com'}
user[0] # David
user[1] # 30
user[2] # mail@mail.com
Otros beneficios que encontramos es en la eficiencia del código, ya que al utilizar tuplas (o nametuples
) encontramos que:
- Las tuplas tienen mayor rendimiento al momento de ser creadas, debido a que son objetos inmutables y su alocación en memoria es mucho ams eficiente que las clasicas lsitas o diccionarios.
- Son ideales para procesar información que debe mantenerse constante durante todo su tiempo de vida, ya que las tuplas son inmutables.
- El tiempo de acceso a los elementos de la tupla es más rápido que las listas o los diccionarios.
Note
Esto no significa que las tuplas sean mejores que otras estructuras de datos, esto depende de cada caso en particular y debes analizarlo antes de tomar una decisión.
Info
Para más información sobre namedtuple
, visita la documentación oficial en este enlace