Introducción a la programación en Python

clase 07b
Módulos pickle y csv

El módulo pickle

Las cadenas de caracteres se escriben y se leen de archivos de manera directa. Pero otro tipo de datos, como los números enteros, dan un poco más de trabajo porque hay que convertirlos a cadena de caracteres para escribirlos con la función f.write, y luego al leerlos con la función f.read es necesario volver a convertirlos en enteros por medio de la función int(). El proceso es aún más complicado si se quiere almacenar estructuras de datos más complejas como listas o diccionarios.

En lugar de escribr complejas secuencias de código que conviertan todos los tipos de datos a cadena de texto, luego verifiquen y vuelvan a restituir el tipo de datos original, el procedimiento correcto es usar el módulo pickle.

Este módulo permite convertir casi cualquier tipo de objeto a una secuencia de bits (serialización) y luego recuperar el objeto original (deserialización). De esta manera, el objeto se puede almacenar en un archivo, enviar a través de una conexión de red, etc.

Para utilizar las funciones del módulo, se lo debe importar mediante la instrucción import pickle. Además, los archivos que vayan a utilizarse deben abrirse en modo binario, 'b', ya que es de la forma en que se guardan los datos.

Las principales funciones son pickle.dump(), para volcar los datos en un archivo, y pickle.load(), para cargar los datos desde un archivo.

pickle.dump()

Esta función recibe dos parámetros: el objeto con los datos a serializar, y el archivo donde se van a escribir.

pickle.dump(obj, f)

El objeto puede ser prácticamente de cualquier tipo, como un número o cadena de caracteres, o una estructura como lista, tupla, set o diccionario. El archivo debe haber sido abierto en modo de escritura binaria 'wb'.

pickle.load()

Esta función recibe como parámetro el archivo de donde se van a leer los datos, y devuelve el objeto deserializado.

datos = pickle.load(f)

Los datos deserializados se pueden asignar a una variable. El archivo debe haber sido abierto en modo de lectura binaria 'rb'.

Ejemplo 7.1

El siguiente ejemplo guarda un diccionario en un archivo usando la función pickle.dump(), y después lo lee del archivo con pickle.load().

In [7]:
import pickle

notas = {0 : "do", 1 : "do#", 2 : "re", 3 : "mib",
         4 : "mi", 5 : "fa", 6 : "fa#", 7 : "sol",
         8 : "lab", 9 : "la", 10 : "sib", 11 : "si"}

f = open('./tmp/archivo_notas','wb')
pickle.dump(notas,f)
f.close()

f = open('./tmp/archivo_notas','rb')
datos = pickle.load(f)
f.close
print(datos)
{0: 'do', 1: 'do#', 2: 're', 3: 'mib', 4: 'mi', 5: 'fa', 6: 'fa#', 7: 'sol', 8: 'lab', 9: 'la', 10: 'sib', 11: 'si'}

El mismo código puede escribirse de forma más compacta y a la vez más robusta, usando la estructura with.

In [8]:
import pickle

notas = {0 : "do", 1 : "do#", 2 : "re", 3 : "mib",
         4 : "mi", 5 : "fa", 6 : "fa#", 7 : "sol",
         8 : "lab", 9 : "la", 10 : "sib", 11 : "si"}

with open('./tmp/archivo_notas', 'wb') as f:
    pickle.dump(notas, f)

with open('./tmp/archivo_notas', 'rb') as f:
    datos = pickle.load(f)
    
print(datos)
{0: 'do', 1: 'do#', 2: 're', 3: 'mib', 4: 'mi', 5: 'fa', 6: 'fa#', 7: 'sol', 8: 'lab', 9: 'la', 10: 'sib', 11: 'si'}

El módulo csv

Los archivos con formato csv (Comma Separated Values) son archivos de texto, que en cada línea contienen valores separados por algún delimitador (por defecto, una coma). Es uno de los formatos más utilizados para importar y exportar datos tales como planillas y bases de datos.

El módulo csv provee varias funciones para el manejo de archivos de este tipo en Python.

El módulo debe ser importado con la instrucción import csv.

csv.reader()

La función csv.reader() recibe como argumento un objeto de tipo file abierto por la función open(). Opcionalmente puede recibir como segundo argumento, el carácter que va a ser considerado el delimitador de cada campo (por defecto, la coma).

Por cada línea del archivo, la función devuelve una lista cuyos elementos son las cadenas de texto separadas por el carácter delimitador.

Consideremos el archivo secuencia.csv ubicado en el directorio tmp, conteniendo las siguientes líneas de texto, que representan una secuencia de notas:

0, 0.75, 72
0.75, 0.5, 62
1.25, 1.25, 59
2.5, 0.25, 56
2.75, 0.25, 67
3.0, 0.25, 70
3.25, 0.5, 66

Cada línea de texto (fila) corresponde a una nota, y las columnas son respectivamente el momento de inicio y la duración, en fracciones de beat, y el número de nota MIDI.

Si abrimos el archivo con la función open(), la función csv.reader() devuelve un objeto iterable con todas las listas generadas por cada línea del archivo:

In [1]:
import csv
f = open('./tmp/secuencia.csv')
r = csv.reader(f)
for fila in r:
    print(fila)
f.close()
['0', ' 0.75', ' 72']
['0.75', ' 0.5', ' 62']
['1.25', ' 1.25', ' 59']
['2.5', ' 0.25', ' 56']
['2.75', ' 0.25', ' 67']
['3.0', ' 0.25', ' 70']
['3.25', ' 0.5', ' 66']

Con la función .join(), podemos imprimir cada lista como una cadena, utilizando el separador que se especifique.

In [2]:
import csv
f = open('./tmp/secuencia.csv')
r = csv.reader(f, delimiter=',')
for fila in r:
    print('\t '.join(fila))
f.close()
0	  0.75	  72
0.75	  0.5	  62
1.25	  1.25	  59
2.5	  0.25	  56
2.75	  0.25	  67
3.0	  0.25	  70
3.25	  0.5	  66

csv.writer()

La función csv.writer() devuelve un objeto tipo writer, con el que se pueden escribir filas a un archivo en formato csv.

La función recibe como argumento el archivo al cual se va a escribir, que debió ser abierto previamente con la función open() en modo de escritura ('w'). Al igual que cvs.reader(), puede recibir como argumento opcional, el carácter a utilizar para delimitar los campos (por defecto, la coma).

Una vez creado el objeto tipo writer, con el método .writerow() se puede escribir una fila al archivo. Típicamente, el método recibe como argumento un iterable, y cada elemento se escribe como un campo separado por el caracter establecido como delimitador en la función csv.writer().

Con el método .writerows() se puede escribir cada elemento de un iterable en una fila nueva.

In [10]:
import csv

f = open("./tmp/secuencia_writerow.csv", "w")
writer = csv.writer(f, delimiter="\t")
writer.writerow([0, 0.25, 66])
writer.writerow([0.5, 0.75, 57])
writer.writerow([1.25, 0.5, 63])
f.close()
In [8]:
import csv

secuencia =  [[0,    0.75,  72],
              [0.75,  0.5,  62],
              [1.25, 1.25,  68]]

f = open("./tmp/secuencia_writerows.csv", "w")
writer = csv.writer(f, delimiter="\t")
writer.writerows(secuencia)
f.close()

DictReader(), DictWriter()

El módulo csv provee funciones similares a las anteriores, para leer y escribir diccionarios como archivos con formato csv.

Consideremos el archivo secuencia_diccionario.csv ubicado en el directorio tmp, conteniendo las siguientes líneas de texto:

inicio, duracion, altura
0,0.75,72
0.75,0.5,62
1.25,1.25,59

La función DictReader() lee el archivo que recibe como argumento, interpretando la primera fila como claves de un diccionario. Por cada línea siguiente se genera un diccionario con esas claves, con los valores que aparecen en la fila:

In [19]:
from csv import DictReader

with open('./tmp/secuencia_diccionario.csv') as f:
    r = DictReader(f)
    for linea in r:
      print(linea)
{'inicio': '0', ' duracion': '0.75', ' altura': '72'}
{'inicio': '0.75', ' duracion': '0.5', ' altura': '62'}
{'inicio': '1.25', ' duracion': '1.25', ' altura': '59'}

La función DictWriter() devuelve un objeto tipo writer, con el que se pueden escribir elementos de un diccionario como filas a un archivo en formato csv.

Al igual que en la función csv.writer(), el método .writerow() permite escribir línea a línea en el archivo.

El método writeheader() es opcional, y escribe una línea con el nombre de los campos o claves.

In [25]:
import csv

secuencia =  [[0,    0.75,  72],
              [0.75,  0.5,  62],
              [1.25, 1.25,  59],
              [2.5,  0.25,  56]]

campos = ['inicio', 'duracion', 'altura']

with open('./tmp/secuencia_diccionario_nuevo.csv', 'w') as f:
    dicc_writer = csv.DictWriter(f, delimiter=',', lineterminator='\n', fieldnames=campos)
    dicc_writer.writeheader()
    for i in range(len(secuencia)):
        dicc_writer.writerow({'inicio': secuencia[i][0], 'duracion': secuencia[i][1], 
                              'altura': secuencia[i][2]})

with open('./tmp/secuencia_diccionario_nuevo.csv', 'r') as f:
    dict = f.read()
print(dict)        
inicio,duracion,altura
0,0.75,72
0.75,0.5,62
1.25,1.25,59
2.5,0.25,56