Python orientado a objetos: serialización de objetos

En el contexto del almacenamiento de datos, la serialización es el proceso de traducir las estructuras de datos o el estado del objeto a un formato que pueda almacenarse (por ejemplo, en un archivo o búfer de memoria) o transmitirse y reconstruirse más tarde.

En la serialización, un objeto se transforma en un formato que se puede almacenar, para luego poder deserializarlo y recrear el objeto original a partir del formato serializado.

Pepinillo

El decapado es el proceso mediante el cual una jerarquía de objetos de Python se convierte en un flujo de bytes (generalmente no legible por humanos) para ser escrito en un archivo, esto también se conoce como serialización. La eliminación es la operación inversa, mediante la cual un flujo de bytes se convierte de nuevo en una jerarquía de objetos de Python en funcionamiento.

Pickle es la forma más sencilla de almacenar el objeto. El módulo Python Pickle es una forma orientada a objetos de almacenar objetos directamente en un formato de almacenamiento especial.

¿Qué puede hacer?

  • Pickle puede almacenar y reproducir diccionarios y listas muy fácilmente.
  • Almacena atributos de objeto y los restaura al mismo estado.

¿Qué pepinillo no puede hacer?

  • No guarda un código de objetos. Solo sus valores de atributos.
  • No puede almacenar identificadores de archivos ni sockets de conexión.

En resumen, podemos decir que el decapado es una forma de almacenar y recuperar variables de datos dentro y fuera de archivos donde las variables pueden ser listas, clases, etc.

Para encurtir algo debes -

  • importar pepinillos
  • Escribe una variable en el archivo, algo como
pickle.dump(mystring, outfile, protocol),

donde el protocolo del tercer argumento es opcional Para eliminar algo, debe -

Importar pepinillos

Escribe una variable en un archivo, algo como

myString = pickle.load(inputfile)

Métodos

La interfaz pickle proporciona cuatro métodos diferentes.

  • dump() - El método dump () se serializa en un archivo abierto (objeto similar a un archivo).

  • dumps() - Serializa a una cadena

  • load() - Deserializa de un objeto abierto.

  • loads() - Deserializa de una cuerda.

Según el procedimiento anterior, a continuación se muestra un ejemplo de "decapado".

Salida

My Cat pussy is White and has 4 legs
Would you like to see her pickled? Here she is!
b'\x80\x03c__main__\nCat\nq\x00)\x81q\x01}q\x02(X\x0e\x00\x00\x00number_of_legsq\x03K\x04X\x05\x00\x00\x00colorq\x04X\x05\x00\x00\x00Whiteq\x05ub.'

Entonces, en el ejemplo anterior, hemos creado una instancia de una clase Cat y luego la hemos decapado, transformando nuestra instancia "Cat" en una matriz simple de bytes.

De esta manera podemos almacenar fácilmente la matriz de bytes en un archivo binario o en un campo de base de datos y restaurarlo a su forma original desde nuestro soporte de almacenamiento en un momento posterior.

Además, si desea crear un archivo con un objeto decapado, puede usar el método dump () (en lugar del dumps * () * uno) pasando también un archivo binario abierto y el resultado de decapado se almacenará en el archivo automáticamente.

[….]
binary_file = open(my_pickled_Pussy.bin', mode='wb')
my_pickled_Pussy = pickle.dump(Pussy, binary_file)
binary_file.close()

Despellejar

El proceso que toma una matriz binaria y la convierte en una jerarquía de objetos se llama desencadenamiento.

El proceso de despegado se realiza utilizando la función load () del módulo pickle y devuelve una jerarquía de objetos completa a partir de una matriz de bytes simple.

Usemos la función de carga en nuestro ejemplo anterior.

Salida

MeOw is black
Pussy is white

JSON

JSON (JavaScript Object Notation) ha sido parte de la biblioteca estándar de Python y es un formato ligero de intercambio de datos. Es fácil para los humanos leer y escribir. Es fácil de analizar y generar.

Debido a su simplicidad, JSON es una forma en la que almacenamos e intercambiamos datos, lo cual se logra a través de su sintaxis JSON y se utiliza en muchas aplicaciones web. Ya que está en formato legible por humanos, y esta puede ser una de las razones para usarlo en la transmisión de datos, además de su efectividad cuando se trabaja con API.

Un ejemplo de datos con formato JSON es el siguiente:

{"EmployID": 40203, "Name": "Zack", "Age":54, "isEmployed": True}

Python simplifica el trabajo con archivos Json. El módulo utilizado para este propósito es el módulo JSON. Este módulo debe estar incluido (integrado) en su instalación de Python.

Entonces, veamos cómo podemos convertir el diccionario de Python a JSON y escribirlo en un archivo de texto.

JSON a Python

Leer JSON significa convertir JSON en un valor (objeto) de Python. La biblioteca json analiza JSON en un diccionario o lista en Python. Para hacer eso, usamos la función load () (cargar desde una cadena), de la siguiente manera:

Salida

A continuación se muestra un archivo json de muestra,

data1.json
{"menu": {
   "id": "file",
   "value": "File",
   "popup": {
      "menuitem": [
         {"value": "New", "onclick": "CreateNewDoc()"},
         {"value": "Open", "onclick": "OpenDoc()"},
         {"value": "Close", "onclick": "CloseDoc()"}
      ]
   }
}}

El contenido anterior (Data1.json) parece un diccionario convencional. Podemos usar pickle para almacenar este archivo, pero su salida no es legible por humanos.

JSON (Java Script Object Notification) es un formato muy simple y esa es una de las razones de su popularidad. Ahora echemos un vistazo a la salida json a través del siguiente programa.

Salida

Arriba abrimos el archivo json (data1.json) para leerlo, obtenemos el controlador de archivos y lo pasamos a json.load y recuperamos el objeto. Cuando intentamos imprimir la salida del objeto, es lo mismo que el archivo json. Aunque el tipo de objeto es diccionario, aparece como un objeto Python. Escribir al json es simple, ya que vimos este problema. Arriba cargamos el archivo json, agregamos otro par de valores clave y lo volvemos a escribir en el mismo archivo json. Ahora, si vemos data1.json, se ve diferente. Es decir, no en el mismo formato que vemos anteriormente.

Para que nuestra Salida se vea igual (formato legible por humanos), agregue el par de argumentos en nuestra última línea del programa,

json.dump(conf, fh, indent = 4, separators = (‘,’, ‘: ‘))

De manera similar a pickle, podemos imprimir la cadena con volcados y cargar con cargas. A continuación se muestra un ejemplo de eso,

YAML

YAML puede ser el estándar de serialización de datos más amigable para todos los lenguajes de programación.

El módulo yaml de Python se llama pyaml

YAML es una alternativa a JSON -

  • Human readable code - YAML es el formato más legible por humanos tanto que incluso su contenido de la página principal se muestra en YAML para hacer este punto.

  • Compact code - En YAML usamos sangría de espacios en blanco para denotar estructura, no corchetes.

  • Syntax for relational data - Para referencias internas usamos anclas (&) y alias (*).

  • One of the area where it is used widely is for viewing/editing of data structures - por ejemplo, archivos de configuración, volcado durante la depuración y encabezados de documentos.

Instalación de YAML

Como yaml no es un módulo integrado, debemos instalarlo manualmente. La mejor manera de instalar yaml en una máquina con Windows es a través de pip. Ejecute el siguiente comando en su terminal de Windows para instalar yaml,

pip install pyaml (Windows machine)
sudo pip install pyaml (*nix and Mac)

Al ejecutar el comando anterior, la pantalla mostrará algo como el siguiente en función de cuál es la última versión actual.

Collecting pyaml
Using cached pyaml-17.12.1-py2.py3-none-any.whl
Collecting PyYAML (from pyaml)
Using cached PyYAML-3.12.tar.gz
Installing collected packages: PyYAML, pyaml
Running setup.py install for PyYAML ... done
Successfully installed PyYAML-3.12 pyaml-17.12.1

Para probarlo, vaya al shell de Python e importe el módulo yaml, importe yaml, si no se encuentra ningún error, entonces podemos decir que la instalación fue exitosa.

Después de instalar pyaml, veamos el siguiente código,

script_yaml1.py

Arriba creamos tres estructuras de datos diferentes, diccionario, lista y tupla. En cada una de las estructuras, hacemos yaml.dump. El punto importante es cómo se muestra la salida en la pantalla.

Salida

La salida del diccionario se ve limpia .ie. valor clave.

Espacio en blanco para separar diferentes objetos.

La lista está marcada con un guión (-)

La tupla se indica primero con !! Python / tuple y luego en el mismo formato que las listas.

Cargando un archivo yaml

Digamos que tengo un archivo yaml, que contiene,

---
# An employee record
name: Raagvendra Joshi
job: Developer
skill: Oracle
employed: True
foods:
   - Apple
   - Orange
   - Strawberry
   - Mango
languages:
   Oracle: Elite
   power_builder: Elite
   Full Stack Developer: Lame
education:
   4 GCSEs
   3 A-Levels
   MCA in something called com

Ahora escribamos un código para cargar este archivo yaml a través de la función yaml.load. A continuación se muestra el código para el mismo.

Como la salida no parece muy legible, la embellezco usando json al final. Compare la salida que obtuvimos y el archivo yaml real que tenemos.

Salida

Uno de los aspectos más importantes del desarrollo de software es la depuración. En esta sección veremos diferentes formas de depuración de Python, ya sea con depurador incorporado o con depuradores de terceros.

PDB - El depurador de Python

El módulo PDB admite el establecimiento de puntos de interrupción. Un punto de interrupción es una pausa intencional del programa, donde puede obtener más información sobre el estado del programa.

Para establecer un punto de interrupción, inserte la línea

pdb.set_trace()

Ejemplo

pdb_example1.py
import pdb
x = 9
y = 7
pdb.set_trace()
total = x + y
pdb.set_trace()

Hemos insertado algunos puntos de interrupción en este programa. El programa se detendrá en cada punto de interrupción (pdb.set_trace ()). Para ver el contenido de una variable, simplemente escriba el nombre de la variable.

c:\Python\Python361>Python pdb_example1.py
> c:\Python\Python361\pdb_example1.py(8)<module>()
-> total = x + y
(Pdb) x
9
(Pdb) y
7
(Pdb) total
*** NameError: name 'total' is not defined
(Pdb)

Presione co continuar con la ejecución del programa hasta el siguiente punto de interrupción.

(Pdb) c
--Return--
> c:\Python\Python361\pdb_example1.py(8)<module>()->None
-> total = x + y
(Pdb) total
16

Eventualmente, necesitará depurar programas mucho más grandes, programas que usan subrutinas. Y a veces, el problema que está tratando de encontrar estará dentro de una subrutina. Considere el siguiente programa.

import pdb
def squar(x, y):
   out_squared = x^2 + y^2
   return out_squared
if __name__ == "__main__":
   #pdb.set_trace()
   print (squar(4, 5))

Ahora, ejecutando el programa anterior,

c:\Python\Python361>Python pdb_example2.py
> c:\Python\Python361\pdb_example2.py(10)<module>()
-> print (squar(4, 5))
(Pdb)

Nosotros podemos usar ?para obtener ayuda, pero la flecha indica la línea que está a punto de ejecutarse. En este punto, es útil presionar s paras para entrar en esa línea.

(Pdb) s
--Call--
>c:\Python\Python361\pdb_example2.py(3)squar()
-> def squar(x, y):

Esta es una llamada a una función. Si desea una descripción general de dónde se encuentra en su código, intente l -

(Pdb) l
1 import pdb
2
3 def squar(x, y):
4 -> out_squared = x^2 + y^2
5
6 return out_squared
7
8 if __name__ == "__main__":
9 pdb.set_trace()
10 print (squar(4, 5))
[EOF]
(Pdb)

Puede presionar n para avanzar a la siguiente línea. En este punto, se encuentra dentro del método out_squared y tiene acceso a la variable declarada dentro de la función .ie xey.

(Pdb) x
4
(Pdb) y
5
(Pdb) x^2
6
(Pdb) y^2
7
(Pdb) x**2
16
(Pdb) y**2
25
(Pdb)

Entonces podemos ver que el operador ^ no es lo que queríamos, sino que necesitamos usar el operador ** para hacer cuadrados.

De esta forma podemos depurar nuestro programa dentro de las funciones / métodos.

Inicio sesión

El módulo de registro ha sido parte de la biblioteca estándar de Python desde la versión 2.3 de Python. Como es un módulo integrado, todos los módulos de Python pueden participar en el registro, de modo que el registro de nuestra aplicación puede incluir su propio mensaje integrado con mensajes del módulo de terceros. Proporciona mucha flexibilidad y funcionalidad.

Beneficios de la tala

  • Diagnostic logging - Registra eventos relacionados con el funcionamiento de la aplicación.

  • Audit logging - Registra eventos para análisis empresarial.

Los mensajes se escriben y registran en niveles de "severidad" y minu

  • DEBUG (debug()) - mensajes de diagnóstico para el desarrollo.

  • INFO (info()) - mensajes estándar de "progreso".

  • WARNING (warning()) - detectó un problema no grave.

  • ERROR (error()) - encontró un error, posiblemente grave.

  • CRITICAL (critical()) - generalmente un error fatal (el programa se detiene).

Veamos el siguiente programa simple,

import logging

logging.basicConfig(level=logging.INFO)

logging.debug('this message will be ignored') # This will not print
logging.info('This should be logged') # it'll print
logging.warning('And this, too') # It'll print

Arriba estamos registrando mensajes en el nivel de gravedad. Primero importamos el módulo, llamamos basicConfig y configuramos el nivel de registro. El nivel que establecimos arriba es INFO. Luego tenemos tres declaraciones diferentes: declaración de depuración, declaración de información y una declaración de advertencia.

Salida de logging1.py

INFO:root:This should be logged
WARNING:root:And this, too

Como la declaración de información está debajo de la declaración de depuración, no podemos ver el mensaje de depuración. Para obtener la declaración de depuración también en la terminal de salida, todo lo que necesitamos cambiar es el nivel basicConfig.

logging.basicConfig(level = logging.DEBUG)

Y en la Salida podemos ver,

DEBUG:root:this message will be ignored
INFO:root:This should be logged
WARNING:root:And this, too

Además, el comportamiento predeterminado significa que si no establecemos ningún nivel de registro, es una advertencia. Simplemente comente la segunda línea del programa anterior y ejecute el código.

#logging.basicConfig(level = logging.DEBUG)

Salida

WARNING:root:And this, too

Python integrado en el nivel de registro son en realidad números enteros.

>>> import logging
>>>
>>> logging.DEBUG
10
>>> logging.CRITICAL
50
>>> logging.WARNING
30
>>> logging.INFO
20
>>> logging.ERROR
40
>>>

También podemos guardar los mensajes de registro en el archivo.

logging.basicConfig(level = logging.DEBUG, filename = 'logging.log')

Ahora todos los mensajes de registro irán al archivo (logging.log) en su directorio de trabajo actual en lugar de la pantalla. Este es un enfoque mucho mejor, ya que nos permite hacer un análisis posterior de los mensajes que recibimos.

También podemos establecer el sello de fecha con nuestro mensaje de registro.

logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s %(levelname)s:%(message)s')

La salida obtendrá algo como,

2018-03-08 19:30:00,066 DEBUG:this message will be ignored
2018-03-08 19:30:00,176 INFO:This should be logged
2018-03-08 19:30:00,201 WARNING:And this, too

Benchmarking

La evaluación comparativa o la creación de perfiles son básicamente para probar qué tan rápido se ejecuta su código y dónde están los cuellos de botella. La principal razón para hacer esto es la optimización.

cronométralo

Python viene con un módulo incorporado llamado timeit. Puede usarlo para cronometrar pequeños fragmentos de código. El módulo timeit utiliza funciones de tiempo específicas de la plataforma para que obtenga los tiempos más precisos posibles.

Por lo tanto, nos permite comparar dos envíos de código tomados por cada uno y luego optimizar los scripts para obtener un mejor rendimiento.

El módulo timeit tiene una interfaz de línea de comandos, pero también se puede importar.

Hay dos formas de llamar a un script. Primero usemos el script, para eso ejecute el siguiente código y vea la salida.

import timeit
print ( 'by index: ', timeit.timeit(stmt = "mydict['c']", setup = "mydict = {'a':5, 'b':10, 'c':15}", number = 1000000))
print ( 'by get: ', timeit.timeit(stmt = 'mydict.get("c")', setup = 'mydict = {"a":5, "b":10, "c":15}', number = 1000000))

Salida

by index: 0.1809192126703489
by get: 0.6088525265034692

Arriba usamos dos métodos diferentes. Es decir, por subíndice y accedemos al valor de la clave del diccionario. Ejecutamos la declaración 1 millón de veces ya que se ejecuta demasiado rápido para datos muy pequeños. Ahora podemos ver el acceso al índice mucho más rápido en comparación con get. Podemos ejecutar el código multiplicado veces y habrá una ligera variación en el tiempo de ejecución para obtener una mejor comprensión.

Otra forma es ejecutar la prueba anterior en la línea de comandos. Vamos a hacerlo,

c:\Python\Python361>Python -m timeit -n 1000000 -s "mydict = {'a': 5, 'b':10, 'c':15}" "mydict['c']"
1000000 loops, best of 3: 0.187 usec per loop

c:\Python\Python361>Python -m timeit -n 1000000 -s "mydict = {'a': 5, 'b':10, 'c':15}" "mydict.get('c')"
1000000 loops, best of 3: 0.659 usec per loop

La salida anterior puede variar según el hardware de su sistema y las aplicaciones que se estén ejecutando actualmente en su sistema.

A continuación podemos usar el módulo timeit, si queremos llamar a una función. Como podemos agregar múltiples declaraciones dentro de la función para probar.

import timeit

def testme(this_dict, key):
   return this_dict[key]

print (timeit.timeit("testme(mydict, key)", setup = "from __main__ import testme; mydict = {'a':9, 'b':18, 'c':27}; key = 'c'", number = 1000000))

Salida

0.7713474590139164