Python Dict vs Asdict

Abdullah Bukhari 21 junio 2023
  1. la biblioteca clases de datos en Python
  2. Por qué dict es más rápido que asdict
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

Artículo relacionado - Python Dataclass