Python Dict vs Asdict
La biblioteca dataclasses
se introdujo en Python 3.7, lo que nos permite crear clases estructuradas específicamente para el almacenamiento de datos. Estas clases tienen propiedades y métodos específicos para manejar los datos y su representación.
la biblioteca clases de datos
en Python
Para instalar la biblioteca de clases de datos
, use el siguiente comando.
pip install dataclasses
A diferencia de una clase normal en Python, las clases de datos
se implementan utilizando los decoradores @dataclass
con clases. Además, la declaración de atributos se realiza mediante sugerencias de tipo, que especifican tipos de datos para los atributos en la clase de datos
.
A continuación se muestra un fragmento de código que pone en práctica el concepto.
# A bare-bones Data Class
# Don't forget to import the dataclass module
from dataclasses import dataclass
@dataclass
class Student:
"""A class which holds a students data"""
# Declaring attributes
# Making use of type hints
name: str
id: int
section: str
classname: str
fatherName: str
motherName: str
# Below is a dataclass instance
student = Student("Muhammad", 1432, "Red", "0-1", "Ali", "Marie")
print(student)
Producción :
Student(name='Muhammad', id=1432, section='Red', classname='0-1', fatherName='Ali', motherName='Marie')
Hay dos puntos a tener en cuenta en el código anterior. Primero, un objeto clase de datos
acepta argumentos y los asigna a miembros de datos relevantes sin un constructor _init_()
.
Esto es así porque la clase de datos
proporciona un constructor _init_()
integrado.
El segundo punto a tener en cuenta es que la declaración print
imprime claramente los datos presentes en el objeto sin ninguna función específicamente programada para hacer esto. Esto significa que debe tener una función _repr_()
alterada.
Por qué dict
es más rápido que asdict
En la mayoría de los casos, en los que hubiera utilizado dict
sin clases de datos, sin duda debería seguir utilizando dict
.
Sin embargo, el asdicto
realiza tareas adicionales durante una llamada de copia que pueden no ser útiles para su caso. Estas tareas adicionales tendrán una sobrecarga que le gustaría evitar.
Esto es lo que hace según la documentación oficial. Cada objeto clase de datos
se convierte primero en un dict
de sus campos como pares nombre: valor
.
Luego, las clases de datos
, dicts
, listas y tuplas son recursivas.
Por ejemplo, si necesita una dictación recursiva de clase de datos
, elija asdict
. De lo contrario, se desperdicia todo el trabajo adicional que implica proporcionarlo.
Si usa asdict
en particular, al modificar la implementación de los objetos contenidos para usar dataclass
cambiará el resultado de asdict
en los objetos externos.
from dataclasses import dataclass, asdict
from typing import List
@dataclass
class APoint:
x1: int
y1: int
@dataclass
class C:
aList: List[APoint]
point_instance = APoint(10, 20)
assert asdict(point_instance) == {"x1": 10, "y1": 20}
c = C([APoint(30, 40), APoint(50, 60)])
assert asdict(c) == {"aList": [{"x1": 30, "y1": 40}, {"x1": 50, "y1": 60}]}
Además, la lógica comercial recursiva no puede manejar referencias circulares de ninguna manera. Si usa clases de datos
para representar, bueno, digamos, un gráfico, o alguna otra estructura de datos con referencias circulares, el asdicto
ciertamente fallará.
@dataclasses.dataclass
class GraphNode:
name: str
neighbors: list["GraphNode"]
x = GraphNode("x", [])
y = GraphNode("y", [])
x.neighbors.append(y)
y.neighbors.append(x)
dataclasses.asdict(x)
# The code will crash here as
# the max allowed recursion depth would have exceeded
# while calling the python object
# in case you're running this on jupyter notebook notice
# that the kernel will restart as the code crashed
Además, asdict
construye un nuevo dict
, el __dict__
, aunque accede directamente al atributo dict
del objeto.
Es importante tener en cuenta que el valor de retorno de asdict
no se verá afectado, de ninguna manera, por la reasignación de los atributos del objeto original.
Además, teniendo en cuenta que asdict
usa campos si agrega atributos a un objeto dataclass
que no se asignan a campos declarados, asdict
no los incluirá.
Por último, aunque los documentos no lo mencionan explícitamente, asdict
llamará a una copia profunda en cualquier cosa que no sea una instancia de “clase de datos”, dict
, lista o tupla.
return copy.deepcopy(instance) # a very costly operation !
La instancia de Dataclass
, los dicts
, las listas y las tuplas pasan por la lógica recursiva, que además crea una copia solo con la dictación recursiva aplicada.
Si está razonablemente bien versado en el paradigma orientado a objetos, entonces sabrá que la copia profunda es una operación costosa en sí misma, ya que inspecciona cada objeto para ver qué necesita ser copiado; la falta de manejo de memorandos esencialmente significa que “adicto” con toda probabilidad podría crear múltiples copias de objetos compartidos en gráficos de objetos no triviales.
Cuidado con tal escenario:
from dataclasses import dataclass, asdict
@dataclass
class PointClass:
x1: object
y1: object
obj_instance = object()
var1 = PointClass(obj_instance, obj_instance)
var2 = asdict(var1)
print(var1.x1 is var1.y1) # prints true
print(var2["x1"] is var2["y1"]) # prints false
print(var2["x1"] is var1.x1) # prints false
Producción :
True
False
False