Clases más Pythonicas con namedtuple



@eduardo_gpg

Número de visitas 674

Tiempo de lectura 3 min

14 Noviembre 2022

En pywombat, mucho se ha hablado acerca de cómo escribir código Pythonico, es decir, código que sigue las reglas de escritura propuestas por la comunidad en Python.🐍

Es por ello que, continuando con el tema, para este nuevo post me gustaría habláramos acerca de un feature integrados en las últimas versiones de Python que nos permite crear clases de una forma rápida y sencilla, y lo mejor de todo, de forma Pythonica.

Será un post sumamente interesante, y si eres un dev Python, sin duda te será de mucha utilidad.

Bien, una vez dicho todo esto, y sin más introducción comencemos con esta nueva entrega.

NameTuple

Del feature del que hablaremos el día de hoy tiene por nombre nametduple. Verás, utilizando la función namedtuple() es posible generar clases. Esta función es una factory function, que podemos encontrar en el módulo collections.

Veamos un par de ejemplos.

>>> from collections import namedtuple

>>> Setting = namedtuple("Setting", ['port', 'name', 'alias'] )

>>> setting = Setting(3000, 'Rails', 'RoR')

Para poder crear una clase es necesario llamar a la función pasando, obligatoriamente, 2 argumentos. Donde el primer argumento no será más que el nombre de la clase, y el segundo argumento será un listado con cada uno de los atributos que deseemos la clase posea.

Es importante mencionar que, estos atributos son 100% requeridos al momento de instanciar un objeto. Los atributos pueden ser pasados por posición o por nombre.

setting = Setting(port=3000, name='Rails', alias='RoR')

Una vez con el objeto creado, seremos capaces de acceder a cada uno de los atributos que este posea.

>>> setting.port
3000

>>> setting.name
'Rails'

>>> setting.alias
RoR

Bastante cool no lo crees? 🥳

Ahora, algo importante que debemos tener en cuenta es que los atributos del objeto son de lectura, por lo cual no podemos modificar su valor una vez el objeto haya sido creado. Si hacemos esto, obtendremos como resultado un error.

>>> setting.port = 8000

Traceback (most recent call last):
    setting.port = 8000
AttributeError: can't set attribute

Esto quiere decir que nuestros objetos son inmutables, lo cual tiene todo el sentido del mundo, ya que utilizamos la función namedtule, y la tuplas, si recordamos, son objetos inmutables. 🛸

Esto nos viene perfecto siempre que deseemos crear objetos los cuales no deseemos modifiquen su valor en tiempo de ejecución, por ejemplo, objetos de configuración.

En este caso dejamos a un lado una tupla convencional para pasar a un objeto mucho más robusto.

# Before

>>> settings = (3000, 'Rails', 'RoR')
>>> settings[0]
3000

>>> settings[1]
'Rails'

>>> settings[2]
'RoR'

De igual forma evitamos que definir una clase donde, como ya sabemos, es posible que el objeto añada o modifique sus atributos.

# Before

class Setting:
    def __init__(self, port, name, alias):
        self.port = port
        self.name = name
        self.alias = alias

setting = Setting(3000, 'Rails', 'RoR'

setting.nuevo_atributo = 'El objeto deja de ser inmutable'

Valores por default

Listo, ya aprendimos que es posible crear una nueva clase mediante la función namedtuple, de igual forma aprendimos que, todos los atributos que definíamos para la clases son 100% obligatorios, sin embargo ¿Qué sucede cuando deseamos que nuestra clase posea ciertos atributos con valores por default? bueno, en esos casos haremos uso del parámetro defaults.

Vamos un ejemplo.

Al momento de crear nuestra clase con la función namedtuple, pasaremos por valor al parámetro defaults una tupla, donde definiremos cada uno de los valores por default que nuestra clase tendrá.

Recordemos que en Python los valores por default se asignan de izquierda a derecha.

Ejemplo.

def __init__(self, port, name='No name', alias='No alias'):
    pass

Por lo tanto, haremos lo mismo para los valores por default.

>>> Setting = namedtuple("Setting", 
    ['port', 'name', 'alias'], 
    defaults=('No Name', 'No Alias') 
)

>>> setting = Setting(port=3000)

>>> setting.port
3000

>>> setting.name
No Name

>>> setting.alias
No Alias

Como podemos observar, para instanciar el objeto ya no son requeridos los valores para name y alias, puesto que estos ya tiene valores pre definidos.

Métodos y atributos del objeto

Afortunadamente namedtuple nos ofrece un par de atributos y métodos para hacer nuestra vida un poco más sencilla.

Por ejemplo, si queremos conocer todos los atributos que posee un objeto, podemos hacer uso del atributo _fields (Con un guion bajo como prefijo).

>>> setting._fields
('port', 'name', 'alias')

Esto nos retornar un tupla de strings con cada uno de los atributos que posee dicho objeto.

Así mismo, si lo que deseamos es convertir nuestro objecto a un diccionario, por ejemplo para que después podamos serializarlo como un objeto JSON podemos utilizar el método _asdict() (Con un guion bajo como prefijo).

>>> setting._asdict()
{'port': 3000, 'name': 'Rails', 'alias': 'RoR'}

Este método retorna un diccionario con todos los atributos y sus correspondiente valores. 🎃

Y por último, un método que puede ser de utilidad es sin duda _replace() (Con un guion bajo como prefijo). Este método crea una nueva instancia de la clase a partir del objeto original con la posibilidad de modificar alguno de sus atributos.

>>> new_setting = setting._replace(port=8000)

>>> new_setting.port
8000

>>> setting.port
3000

Recordemos, el objeto como tal no es mutable, sin embargo nada nos impide generar otro objeto a partir de una base.


¿El contenido te resulto de ayuda?

Para poder dejar tu opinión es necesario ser un usuario autenticado. Login

Más Tips y Ejercicios 🐍

Adquiere una subscripción PyWombat por tan solo $3 USD. al mes.

Conoce los beneficios de ser usuario premium:
Niveles desbloqueados: Ten accesos a todos los niveles de ejercicios. 🔓
Nuevo límite: Incrementa tu límite de ejercicios por semana. 🚀
Contenido único: Recibe semanalmente recursos exclusivos de Python (Videos, Artículos y Capitulos del libro PyWombat, comienza como desarrollador Python. 🐍