Clase 6

Temas

  • Scripting
  • Manejo de archivos
  • Persistencia de datos

Scripting

Python se instala por defecto en la mayoría de las distribuciones de Linux. En Mac y Windows debemos instalarlo manualmente.

  • La mayoría de las implementaciones son interpretadas.
  • Python “viene con las baterías incluidas”.
  • Es integrable con otros lenguajes de programación.

  • Documentación de todos los modulos de la librería estandar: https://docs.python.org/2/library/

Ya hemos usado

Cuando vimos unit testing usamos la variable __name__

if __name__ == __main__:
        pass

Solo se ejutará si se encuentra en el módulo principal

Módulos útiles

  • os: provee funcionalidades para utilizar herramientas provistas por el sistema operativo.
  • os.path: provee: funcionalidades para manipular nombres de archivos y directorios.
  • sys: provee acceso a variables provistas por el intérprete.
  • io: provee herramientas para trabajar con streams.
  • ¡Funciones de la librería estándar!

Más funcionalidades útiles:

  • os.getcwd()
  • os.listdir()
  • os.chdir()
  • os.link()
  • os.mkdir()
  • os.remove() == os.unlink()
  • os.removedirs()
  • os.rename()
  • os.rmdir()

Veamos algunas

In [ ]:
help(os.getcwd)
In [ ]:
os.getcwd()
In [ ]:
help(os.listdir)
In [ ]:
os.listdir(".")
In [ ]:
help(os.chdir)
In [ ]:
print os.getcwd()
os.chdir("..")
print os.getcwd()
In [ ]:
help(os.link)
In [ ]:
help(os.mkdir)
In [ ]:
help(os.remove)
help(os.unlink)

Ejercicio 6.1

Implementar un script que liste por pantalla, en orden alfabético, el contenido del directorio donde se ejecuta.

Solución 6.1

In [ ]:
import os

def listar_contenido_carpeta_usuario():

    for archivo in os.listdir('.'):
        print(archivo)
    
if __name__ == "__main__":
    listar_contenido_carpeta_usuario()

Más funcionalidades útiles:

  • os.path.dirname()
  • os.path.exists()
  • os.path.getsize()
  • os.path.isfile()
  • os.path.isdir()
  • os.path.join()
  • os.path.split()
  • os.path.splitdrive()
In [ ]:
help(os.path.dirname)
In [ ]:
os.path.dirname("/Users/miguelalfaro/Desktop/Curso/CursoPython")
In [ ]:
help(os.path.exists)
In [ ]:
print os.path.exists("/Users/miguelalfaro/Desktop/Curso")
print os.path.exists("/Users/miguelalfaro/Desktop/Fotos")
In [ ]:
help(os.path.getsize)
In [ ]:
print os.getcwd()
os.path.getsize("Notebooks/Clase 6.ipynb")
In [ ]:
help(os.path.isfile)
In [ ]:
help(os.path.isdir)
In [ ]:
os.path.isfile("Notebooks/Clase 6.ipynb")
In [ ]:
os.path.isdir("Notebooks/Clase 6.ipynb")
In [ ]:
os.path.isfile("Notebooks")
In [ ]:
os.path.isdir("Notebooks")
In [ ]:
help(os.path.join)
In [ ]:
help(os.path.split)
In [ ]:
os.path.join("/Users/miguelalfaro/Desktop", "Fotos")
In [ ]:
os.path.split("/Users/miguelalfaro/Desktop/Curso")

Ejercicio 6.2

Implementar un script que imprima en pantalla la cantidad de directorios existentes en el directorio donde se encuentra el script

Solución 6.2

In [ ]:
#Solucion 6.2
import os

def cantidad_de_directorios():
    cant = 0
    for directorio in os.listdir('.'):
        if os.path.isdir(directorio):
            cant += 1
    return cant

if __name__ == '__main__':
    print cantidad_de_directorios()

Ejercicio 6.3

Implementar un script que imprima en pantalla el nombre del archivo más pesado del directorio temporal.

Solución 6.3

In [ ]:
import os

def archivo_mas_pesado():
    max_tam = 0
    archivo_max_tam = ""
    for archivo in os.listdir('.'):
        
        if not os.path.isfile(archivo):
            continue
            
        tam = os.path.getsize(archivo)
        
        if tam > max_tam:
            max_tam = tam
            archivo_max_tam = archivo
            
    return archivo_max_tam,tam

if __name__ == '__main__':
    print archivo_mas_pesado()
    

Aún más funcionalidades útiles:

  • sys.argv
  • sys.getdefaultencoding()
  • sys.getrefcount()
In [ ]:
import sys
help(sys.argv)
In [ ]:
help(sys.getdefaultencoding)
In [ ]:
help(sys.getrefcount)

Ejercicio 6.4

Implementar un script que reciba por parámetro una cantidad n de cadenas y que cree una carpeta con el nombre de cada una en el directorio temporal del usuario.

Solución 6.4

In [ ]:
import os

def crear_carpetas(nombres_carpetas):

    for nombre_carpeta in nombres_carpetas:
        os.mkdir(nombre_carpeta)

if __name__ == "__main__":
    crear_carpetas(["directorio1", "directorio2"])

Formato de cadenas

Recordemos:

  • Por posición:
    >>> 'El cuadrado de {0} es {1}.'.format(str(4), str(4**2))
      'El cuadrado de 4 es 16.'
    
  • Por nombre:
    >>> "Mañana será {dia} de {mes}".format(dia=str(3), mes="agosto")
      'Mañana será 3 de agosto'
    
  • Por atributos:
    >>> 'El número complejo es {z.real} + {z.imag}i.'.format(z=3+2j)
      'El número complejo es 3.0 + 2.0i.'
    

Sintaxis más general:

"{" [nombre_campo] ["!" conversión] [":" formato] "}"

Conversión:

Caracter Función
r repr()
s str()
a ascii()

Resulta, entonces:

>>> "{0!s}".format(A()) == "{0}".format(str(A()))
       True

Todas la opciones disponibles en la documentación oficial: https://docs.python.org/2/library/string.html#format-string-syntax

Formato:

[[relleno]alineación][signo][#][0][ancho][,][.precisión][tipo]

>>> '{:*^30}'.format('centered')
     '***********centered***********'

>>> "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42)
'int: 42; hex: 2a; oct: 52; bin: 101010'

>>> for align, text in zip('<^>', ['left', 'center', 'right']): '{0:{fill}{align}16}'.format(text, fill=align, align=align)
     'left<<<<<<<<<<<<'
     '^^^^^center^^^^^'
     '>>>>>>>>>>>right'

Para sólo alinear usamos las funciones de cadena:

Función Uso
.rjust(n) Justifica a derecha n columnas
.ljust(n) Justifica a izquierda n columnas
.center(n) Centra n columnas
>>> for i, v in enumerate(['a', 'b', 'c', 'd']): print(str(i).rjust(5)), v.rjust(5))
0a 1b 2c 3d

Vieja sintaxis de formato:

>>> "El cuadrado de %d es %d." % (4, 4**2)
'El cuadrado de 4 es 16.'

>>> 'Mañana será %d de %s.' % (3, "agosto")
'Mañana será 3 de agosto.'

Ejercicio 6.5

Imprimir por pantalla una tabla de conversión de grados Celsius a Farenheit y Kelvin, sabiendo que:

°K = °C + 273,15 °F = 9/5 ∙ °C + 32

Implementar la tabla desde 0°C hasta 200°C considerando incrementos de a 20 °C.

Solución 6.5

In [84]:
def celsius_a_kelvin(temp_celsius):
    return temp_celsius + 273.15

def celsius_a_farenheit(temp_celsius):
    return temp_celsius * (9/5) + 32

def imprimir_tabla_conversion_temperaturas():
    print("C".center(8), "F".center(8), "K".center(8))
    for temp_c in range(0, 201, 20):
        temp_f = celsius_a_farenheit(temp_c)
        temp_k = celsius_a_kelvin(temp_c)
        #print("{0:^8} {1:^8} {2:^8}".format(temp_c, temp_f, temp_k))
        print(str(temp_c).rjust(8), str(temp_f).rjust(8), str(temp_k).rjust(8))

imprimir_tabla_conversion_temperaturas()
('   C    ', '   F    ', '   K    ')
('       0', '      32', '  273.15')
('      20', '      52', '  293.15')
('      40', '      72', '  313.15')
('      60', '      92', '  333.15')
('      80', '     112', '  353.15')
('     100', '     132', '  373.15')
('     120', '     152', '  393.15')
('     140', '     172', '  413.15')
('     160', '     192', '  433.15')
('     180', '     212', '  453.15')
('     200', '     232', '  473.15')

Ejercicio 6.6

Escribir una función que imprima por pantalla una tabla con nombre – tipo (“a” para archivos, “d” para directorios) – peso para el contenido del directorio de prueba.

Nombre              Tipo  Tamaño
archivo no vacio     a      14
archivo vacio        a      14
directorio 1         d       0
directorio 2         d       0
hola mundo.txt       a      20
lorem 1.txt          a     682
lorem 2.txt          a    1451
lorem 3.txt          a     100

Solución 6.6

In [85]:
import os

def obtener_tipo(nombre_archivo):
    ''' Devuelve 'a' en caso de que el nombre pasado por parámetro
    sea un archivo, 'd' en caso de que sea un directorio.
    '''
    return 'a' if os.path.isfile(nombre_archivo) else 'd'

def imprimir_info_directorio_prueba():
    # Cambia el directorio de trabajo.
    path = os.path.join(os.getcwd(), "ejemplos/clase6", "Directorio de prueba")
    os.chdir(path)

    # Imprime la tabla
    formato_tabla = "{0:<20} {1:<6} {2:<20}"
    print(formato_tabla.format("Nombre", "Tipo", "Tamaño"))
    for archivo in os.listdir(path):
        print(formato_tabla.format(archivo, obtener_tipo(archivo), os.path.getsize(archivo)))


imprimir_info_directorio_prueba()
Nombre               Tipo   Tamaño             
.DS_Store            a      6148                
archivo no vacio     a      14                  
archivo vacio        a      14                  
directorio 1         d      102                 
directorio 2         d      102                 
hola mundo.txt       a      20                  
lorem 1.txt          a      682                 
lorem 2.txt          a      1451                
lorem 3.txt          a      100                 

Archivos - Lectura

  • Apertura de archivos: usamos la función archivo = open(nombre_archivo, modo).
Modo Función
'r' Sólo lectura (defecto)
'w' Sólo escritura
'a' Agregar al final
'r+' Lectura y escritura
'b' Binario
  • Cerrado de archivos: archivo.close()

Forma de uso:

try:
    fp = open(nombre_archivo)
except IOError:
    # Manejar error.
    pass
else:
    # Procesar archivo fp.close()

Solemos usar el operador with para manejar archivos, reemplazando try / finally:

with open(nombre_archivo) as fp:
        # Código de ejecución, usando fp pass

Así nos aseguramos de cerrarlos siempre.

Métodos para leer desde archivos:

  • read() / seek() / tell()
  • readline()
  • readlines() / list(fp)
  • for linea in fp: ...

¡Python nos abstrae de los tipos de salto de línea, siempre vamos a usar ‘\n’!

Usamos los métodos de cadena para ignorar el salto de línea:

  • lstrip() / rstrip() / strip()

Otras métodos de cadenas que pueden servir:

  • index() / split()

Ejercicio 6.7

Implementar un programa que a partir del archivo de letra / número “tabla1.txt” del directorio “Archivos de prueba”, devolver un diccionario en donde las claves sean los valores posibles de las letras (“a”, “b”, “c”, “d”, “e”) y en donde los valores asociados del diccionario sean la suma para cada letra de los números del archivo.

Solución 6.7

In [24]:
import os

def crear_diccionario_valores():

    # Procesa el archivo.
    try:
        archivo = open("tabla1.txt")
    except IOError as ioe:
        print("No se encuentra el archivo!")
    else:
        tabla = {}
        for linea in archivo:
            valores = linea.rstrip().split()
            letra = valores[0]
            numero = int(valores[1])
            tabla[letra] = tabla.get(letra, 0) + numero
        archivo.close()
        return tabla
       


print(crear_diccionario_valores())
No se encuentra el archivo!
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-24-3ee8c8b126c5> in <module>()
     20 
     21 
---> 22 print(crear_diccionario_valores())

<ipython-input-24-3ee8c8b126c5> in crear_diccionario_valores()
     10 
     11     tabla = {}
---> 12     for linea in archivo:
     13         valores = linea.rstrip().split()
     14         letra = valores[0]

UnboundLocalError: local variable 'archivo' referenced before assignment

Ejercicio 6.8

Implementar un programa que devuelva un diccionario de configuraciones a partir del archivo de entrada “parametros.txt”

Solución 6.8

In [11]:
import os

def obtener_parametros():

    # Procesa el archivo.
    parametros = {}
    with open("parametros.txt") as archivo:
        for linea in archivo:
            parametro = linea.split("=")
            parametro = map(str.strip, parametro)

            parametros[parametro[0]] = parametro[1]

    return parametros


print(obtener_parametros())
{'status': 'openvpn-status.log', 'persist-key': '1', 'comp-lzo': '1', 'dh': 'dh1024.pem', 'proto': 'udp', 'ca': 'ca.crt', 'mute': '20', 'server-bridge': '10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100', 'dev': 'tap0', 'persist-tun': '32', 'cert': 'server.crt', 'verb': '3', 'key': 'server.key', 'ifconfig-pool-persist': 'ipp.txt', 'max-clients': '100', 'port': '1194', 'keepalive': '10 120'}

Archivos - Escritura

Para abrir un archivo para escritura se debe hacer de alguno de los siguientes modos:

Modo Función
'w' Sólo escritura
'a' Agregar al final
'r+' Lectura y escritura

De la misma forma que para la lectura, existen dos formas distintas de escribir un archivo:

  • write(cadena)
  • writelines(lista_de_cadenas)

Asi como la función readline devuelve las líneas con los caracteres de fin de línea (\n), será necesario agregar los caracteres de fin de línea a las cadenas que se vayan a escribir.

In [41]:
with open("saludo.py", "w") as saludo:
    saludo.write("# Este programa fue generado por otro programa!\n")
    saludo.write("print('Hola Mundo')\n")

Atención

Si un archivo existente se abre en modo lectura-escritura, al escribir en él se sobreescribirán los datos anteriores, a menos que se haya llegado al final del archivo.

Agregar información a un archivo

  • Puede resultar raro, pero es bastante útil
  • Se utiliza para escribir archivos tipo log
In [18]:
archivo_log = open("log.txt", 'a')
# ...
# Hacer cosas que pueden dar error
error = "Formato de entrada invalido\n"
###
if error:
    archivo_log.write("ERROR: " + error)
# ...
# Finalmente cerrar el archivo
archivo_log.close()

Usando writelines

In [20]:
archivo_log = open("log.txt", 'a')
# ...
# Hacer cosas que pueden dar error

#Hardcodeo errores
error1 = "Formato de entrada invalido\n"
error2 = "No se pudo acceder al recurso\n"
lista_de_errores = ["ERROR: " + error1, "ERROR: " + error2]

###
if lista_de_errores:
    archivo_log.writelines(lista_de_errores)
# ...
# Finalmente cerrar el archivo
archivo_log.close()  

Ejercicio 6.9

Escribir una función que reciba el nombre de un archivo y una temperatura, convierta la temperatura a Kelvin y Farenheit y escriba los 3 valores en el archivo.

exportar_conversion_temperaturas("tempraturas.txt", 37)

def celsius_a_kelvin(temp_celsius):
    return temp_celsius + 273.15

def celsius_a_farenheit(temp_celsius):
    return temp_celsius * (9/5) + 32
C:37
F:69
K:310.15

Solución 6.9

In [43]:
def celsius_a_kelvin(temp_celsius):
    return temp_celsius + 273.15

def celsius_a_farenheit(temp_celsius):
    return temp_celsius * (9/5) + 32

def exportar_conversion_temperaturas(nombre_salida, temp_c):
    archivo = open(nombre_salida, "a")
    temp_f = celsius_a_farenheit(temp_c)
    temp_k = celsius_a_kelvin(temp_c)
    lista_temp = ["C:" + str(temp_c) + '\n', "F:" + str(temp_f) + '\n', "K:" + str(temp_k) + '\n']
    archivo.writelines(lista_temp)

exportar_conversion_temperaturas("tempraturas.txt", 37)
exportar_conversion_temperaturas("tempraturas.txt", 12)

Ejercicio 6.10

El archivo de prueba “materias.txt” contiene un listado de materias.

Sabiendo que cada línea tiene el formato:

     `ddcc – NOMBRE MATERIA`

Siendo dd el número del departamento y cc el código de la materia; se pide crear archivos de nombre dd.txt con un listado de los nombres de las materias de cada departamento.

Solución 6.10

In [28]:
import os

def exportar_materias_por_departamento():
    
    # Itera por departamento.
    materias_por_depto = {}
    with open("materias.txt") as materias:
        for materia in materias:
            num_depto = materia[:2]
            materia = materia[7:]

            materias_por_depto[num_depto] = materias_por_depto.get(num_depto, []) + [ materia ]
        
    # Exporta.
    for depto in materias_por_depto:
        with open(depto + ".txt", "w") as salida:
            for materia in materias_por_depto[depto]:
                salida.write(materia)
        

exportar_materias_por_departamento()
---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
<ipython-input-28-b48104bd9c08> in <module>()
     19 
     20 
---> 21 exportar_materias_por_departamento()

<ipython-input-28-b48104bd9c08> in exportar_materias_por_departamento()
      5     # Itera por departamento.
      6     materias_por_depto = {}
----> 7     with open("materias.txt") as materias:
      8         for materia in materias:
      9             num_depto = materia[:2]

IOError: [Errno 2] No such file or directory: 'materias.txt'

Archivos CSV

Un formato que suele usarse para transferir datos es CSV (comma separated values): valores separados por comas.

  • Es bastante sencillo tanto como para leerlo o procesarlo

Un ejemplo:

Nombre,Apellido,Telefono,Cumpleaños
"John","Smith","555-0101","1973-11-24"
"Jane","Smith","555-0101","1975-06-12"
  • En la primer línea del archivo se tiene todos los nombres de los datos, es opcional para el procesamiento pero nos ayuda a entender la información
  • En las siguientes lineas se ingresan los datos, cada campo separado por comas
  • En Python es bastante sencillo procesar este tipo de archivos, tanto para lectura como para escritura con el módulo CSV

Métodos ejecutables del módulo csv:

  • csv.reader(): devuelve un iterable con una lista por iteración.
  • csv.DictReader(): devuelve un iterable con un diccionario por iteración.
  • csv.writer()
  • archivo.writerow()
  • archivo.writerows()
  • csv.DictWriter()

Ejemplo: módulo para guardar y recuperar puntajes de un archivo csv

In [57]:
import csv

def guardar_puntajes(nombre_archivo, puntajes):
    """Guarda la lista de puntajes en el archivo."""
    with open(nombre_archivo, "w") as f:
        archivo_csv = csv.writer(f)
        archivo_csv.writerow(["Nombre", "Puntaje", "Tiempo"])
        archivo_csv.writerows(puntajes)
        
def recuperar_puntajes(nombre_archivo):
    """Recupera los puntajes a partir del archivo provisto."""
    puntajes = []
    with open(nombre_archivo, "r") as f:
        archivo_csv = csv.reader(f)
        archivo_csv.next()
        for nombre, puntaje, tiempo in archivo_csv:
            puntajes.append((nombre, int(puntaje), tiempo))
    return puntajes

guardar_puntajes("archivo.csv", [["Miguel",424,323], ["Mati",423,121]])
puntajes = recuperar_puntajes("archivo.csv")
print puntajes
[('Miguel', 424, '323'), ('Mati', 423, '121')]

Ejercicio 6.11

Escribir en un archivo csv la tabla de información del directorio en donde se encuentra el script. En el formato:

Nombre, Tipo, Tamaño

Tipo:

    `a`: si es un archivo
    `d`: si es un directorio

Ayuda:

  • import os
  • os.listdir(".") #Devuelve una lista con los paths de archivos y directorios locales
  • os.path.isfile(path) #Devuelve True si es un archivo
  • os.path.getsize(path) #Devuelve el tamaño del archivo

Solución 6.11

In [58]:
import csv
import os

def obtener_tipo(nombre_archivo):
    ''' Devuelve 'a' en caso de que el nombre pasado por parámetro
    sea un archivo, 'd' en caso de que sea un directorio.
    '''
    return 'a' if os.path.isfile(nombre_archivo) else 'd'

def exportar_info_directorio_prueba(archivo_salida):

    with open(archivo_salida, 'w') as archivo:
        salida = csv.writer(archivo, lineterminator='\n')
        salida.writerow(["Nombre", "Tipo", "Tamaño"])
        for archivo in os.listdir("."):
            salida.writerow([archivo, obtener_tipo(archivo), os.path.getsize(archivo)])

exportar_info_directorio_prueba("salida.txt")

Ejercicio 6.12

El archivo “paises.csv” contiene información relacionada a los paises del mundo. Escribir en un nuevo archivo csv un listado ordenado alfabéticamente de los nombres de los paises que empiezan con “A” y sus capitales.

Solución 6.12

In [60]:
import csv
import os

def obtener_datos_paises_con_a():
    
    # Itera por pais.
    paises_con_a = []
    with open("paises.csv") as archivo_paises:
        paises = csv.DictReader(archivo_paises)
        for pais in paises:
            if pais['Common Name'][0] in ('a', 'A'):
                paises_con_a.append([pais['Common Name'], pais['Capital']])

    paises_con_a.sort(key = lambda p: p[0])

    with open("salida.csv", 'w') as archivo_salida:
        salida = csv.writer(archivo_salida, lineterminator='\n')
        salida.writerows(paises_con_a)
                
obtener_datos_paises_con_a()

Persistencia

Pickle

  • El módulo pickle implementa un algoritmo que permite serializar y de-serializar structuras de objetos de Python.

  • "Pickling" es el proceso por el cual una jerarquía de objetos de Python es convertida en un stream de bytes.

  • "Unpickling" es el proceso inverso, en el cual a partir de un stream de bytes se obtiene una jerarquía de objetos.

  • Para usar esta librería se debe importar:

    import pickle
    
  • Los archivos generados solo funcionan para Python!.

Funciones principales

  • dump(obj, file, protocol=None)

    Recibe un objeto obj de python, genera un stream de bytes que lo represente y lo guarda en el archivo abierto file (con permisos de escritura).

  • load(file)

    Recibe un archivo abierto file (con permisos de lectura) donde está guardado un stream de bytes asociado a un objeto de python, reconstruye el objeto original y lo devuelve.

In [62]:
import pickle

diccionario = { 'elemento1' : "elemento1",
               'elemento2' : 2,
               'elemento3' : (1,2,3),
               'elemento4' : [1,2,3,4]} 

pickle_out = open("dicc.pickle","wb")
pickle.dump(diccionario,pickle_out)
pickle_out.close()
In [64]:
import pickle

pickle_in = open("dicc.pickle","rb")
pickled_dicc = pickle.load(pickle_in)
pickle_in.close()

print pickled_dicc
{'elemento4': [1, 2, 3, 4], 'elemento1': 'elemento1', 'elemento2': 2, 'elemento3': (1, 2, 3)}

Observaciones:

  • Al serializar una instancia de una clase definida por el usuario, al deserializarla debe estar definida la clase (importando el módulo donde se la define).
  • Al serializar una clase, al deserializarla debe estar definida la clase (importando el módulo donde se la define).
  • Al serializar una función, al deserializarla debe estar definida la función (importando el módulo donde se la define).
  • No se pueden serializar funciones lambda, ni nested functions.

  • Si se busca alta performance se recomienta usar cPickle ( import cPickle as pickle )

JSON (JavaScript Object Notation)

  • Es un formato de archivos (open-standard) que usa text legible por las personas para transmitir datos que consisten en pares atributos-valor y arreglos (u otro valor serializable).

  • Si bien se deriva de JavaScript, JSON es un formato de datos independiente del lenguaje (al día de hoy la mayoría de los lenguajes pueden codificar y decodificar JSON).

  • La librería json de Python usa una API similar a la de otras librerías como pickle.

    import json
    

Funciones principales

  • dump(obj, file, <parámetros personalizables>)

    Recibe un objeto obj de python, serializa el objeto como un JSON (usando la tabla de conversión) y lo guarda en el archivo abierto file (con permisos de escritura).

  • load(file, <parámetros personalizables>)

    Recibe un archivo abierto file (con permisos de lectura) donde está guardado la serialización asociada a un objeto, deserializa (usando la tabal de conversión) y lo devuelve.

Tablas de conversión

  • Serialización
Python JSON
dict object
list, tuple array
str, unicode string
int, long, float number
True true
False false
None null
  • Deserialización
JSON Python
object dict
array list
string unicode
number (int) int, long
number (real) float
true True
false False
null None
In [65]:
import json

diccionario = { 'elemento1' : "elemento1",
               'elemento2' : 2,
               'elemento3' : (1,2,3),
               'elemento4' : [1,2,3,4]} 

json_out = open("dicc.json","w")
json.dump(diccionario,json_out)
json_out.close()
In [66]:
import json

json_in = open("dicc.json","r")
json_dicc = json.load(json_in)
json_in.close()

print json_dicc

#Observaciones:
# Las tuplas se serializan/deserializan como listas
# Los strings se serializan/deserializan como unicodes
{u'elemento4': [1, 2, 3, 4], u'elemento1': u'elemento1', u'elemento2': 2, u'elemento3': [1, 2, 3]}

Observaciones:

  • Solo se pueden serializar los tipos antes mencionados.
  • Para poder serializar y deserializar instancias de otras clases se puede implementar un Encoder y un Decoder personalizados.
In [74]:
import json

class ClaseDePrueba(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
class JSONEncoderClaseDePrueba(json.JSONEncoder):
    def default(self, obj):
        if (isinstance(obj,ClaseDePrueba)):
            #return {"a":obj.a, "b":obj.b}
            # La siguiente linea la veremos en detalle luego
            return {"a":obj.a, "b":obj.b, "_type": "ClaseDePrueba"}
        # Let the base class default method raise the TypeError
        return super().default(obj)
    
instancia = ClaseDePrueba(1,2)

json_out = open("instancia.json","w")
json.dump(instancia,json_out,cls = JSONEncoderClaseDePrueba)
json_out.close()
In [72]:
json_in = open("instancia.json","r")
json_instancia = json.load(json_in)
json_in.close()
print json_instancia
{u'a': 1, u'_type': u'ClaseDePrueba', u'b': 2}
In [81]:
def object_hook_ClaseDePrueba(o):
    if "_type" not in o:
        return o
    tipo = o["_type"]
    if tipo == "ClaseDePrueba":
        return ClaseDePrueba(o["a"],o["b"])
    return o

json_in = open("instancia.json","r")
json_instancia = json.load(json_in, object_hook = object_hook_ClaseDePrueba)
json_in.close()

print type(json_instancia)
print json_instancia.a
print json_instancia.b
<class '__main__.ClaseDePrueba'>
1
2

Próxima Clase

  • Expresiones regulares
  • Procesamiento de XML
  • Scripts y streams
  • Código C embebido en programas Python
  • Introspección