45
Programación en Python Parte 3: Persistencia y BB.DD. Mariano Reingart [email protected]

Programacion en python_3

Embed Size (px)

Citation preview

Page 1: Programacion en python_3

Programación en Python

Parte 3: Persistencia y BB.DD.Mariano Reingart

[email protected]

Page 2: Programacion en python_3

Persistencia y serialización: picklePickle: convertir objeto python en secuencia de bytesFunciones de pickle:

dumps(objeto, proto): serializa a un stringdump(objeto, archivo, proto): guarda en archivoloads(cadena, proto): des-serializa un stringload(archivo, proto): carga desde archivo

>>> import pickle>>> s = pickle.dumps({1:'a',2:'b'},0) >>> s "(dp0\nI1\nS'a'\np1\nsI2\nS'b'\np2\ns.">>> print pickle.loads(s) {1: 'a', 2: 'b'}

Page 3: Programacion en python_3

Persistencia y serialización: pickleimport pickledata1 = {'a': [1, 2.0, 3, 4+6j], 'b': ('string', u'Unicode'),'c': None} selfref_list = [1, 2, 3] selfref_list.append(selfref_list) output = open('data.pkl', 'wb')pickle.dump(data1, output) # Serializar un dict pickle.dump(selfref_list, output, -1) # Serializar list output.close() pkl_file = open('data.pkl', 'rb') data1 = pickle.load(pkl_file) # Deserializar el dict pprint.pprint(data1) data2 = pickle.load(pkl_file) # Deserializar la lista pprint.pprint(data2) pkl_file.close()

Page 4: Programacion en python_3

Persistencia y serialización: shelveShelve: objeto similar a un diccionario persistente

open(filename, flag='c', protocol=None, writeback=False): crea un diccionario persistenteflag= 'r': solo lectura, 'w': lectura/escritura, 'c': creación y lectura/escritura, 'n': nuevoShelve.sync(): sincronizar (writeback=True)Shelve.close(): grabar y cerrar diccionario

>>>import shelve>>> d = shelve.open(filename) # abrir archivo >>> d['clave'] = 'datos' # almacenar datos en una clave >>> data = d['clave'] # leer una COPIA de los datos >>> del d['clave'] # borra los datos almacenados

Page 5: Programacion en python_3

Ejemplo práctico: universidad.pyEnunciado: administrar las cursadas y calificaciones de los alumnos de una universidad "simplificada".

Profesores (dni, nombre, apellido, legajo)Materias (código, nombre)Cursos (materia, año, profesor)Exámenes (materia, nota, fecha)Alumnos (dni, nombre, apellido, nro_matrícula, cursos, exámenes)Universidad (alumnos, profesores, plan_estudio)

Page 6: Programacion en python_3

Ejemplo práctico: universidad.py

Diagrama de clases:

Page 7: Programacion en python_3

Ejemplo práctico: universidad.pyclass Persona: "Clase que representa una persona" def __init__(self, dni, nombre, apellido): "Constructor del objeto" self.dni = dni self.nombre = nombre self.apellido = apellido def __str__(self): "Devuelvo la representación como string" return "%s, %s" % (self.nombre, self.apellido) def __repr__(self): "Devuelve la representación interna" return "Persona(dni=%s, nombre=%s, apellido=%s)" % ( repr(self.dni), repr(self.nombre),...) def __hash__(self): "Devuelvo el identificador único del objeto" return self.dni

Page 8: Programacion en python_3

Ejemplo práctico: universidad.pyclass Profesor(Persona):

def __init__(self, dni, nombre, apellido, legajo): Persona.__init__(self, dni, nombre, apellido) self.legajo = legajo

Page 9: Programacion en python_3

Ejemplo práctico: universidad.pyclass Materia: "Clase para representar una materia"

def __init__(self, codigo, nombre): self.codigo = codigo self.nombre = nombre

def __str__(self): return "%s (%s)" % (self.nombre, self.codigo)

def __repr__(self): return "Materia(codigo=%s, nombre=%s)" % ( repr(self.codigo), repr(self.nombre))

def __hash__(self): return self.codigo

Page 10: Programacion en python_3

Ejemplo práctico: universidad.pyclass Curso: def __init__(self, materia, anio, profesor, cupo=30): self.materia = materia self.anio = anio self.profesor = profesor self.cupo = cupo self.alumnos = []

def inscribir(self, alumno): if self.cupo<len(self.alumnos): raise RuntimeError("No hay mas lugar!") self.alumnos.append(alumno) alumno.inscribir(curso=self)

def __repr__(self): return "Curso(materia=%s, profesor=%s)" % ( repr(self.materia), repr(self.profesor))

Page 11: Programacion en python_3

Ejemplo práctico: universidad.pyclass Examen: def __init__(self, materia, nota, fecha): self.materia = materia self.nota = nota self.fecha = fecha

@property def aprobado(self): return self.nota >= 4

def __repr__(self): return "Examen(materia=%s, nota=%s, fecha=%s)" % (repr(self.materia), repr(self.nota), repr(self.fecha))

Page 12: Programacion en python_3

Ejemplo práctico: universidad.pyclass Alumno(Persona): "Clase para representar un Alumno"

def __init__(self, dni, nombre, apellido, nro_matricula): Persona.__init__(self, dni, nombre, apellido) self.nro_matricula = nro_matricula self.cursos = [] # lista de materias cursadas self.examenes = [] # lista de exámenes rendidos

def inscribir(self, curso): self.cursos.append(curso)

def rendir(self, materia, nota, fecha): examen = Examen(materia, nota, fecha) # agrego el examen a la lista de los exámenes self.examenes.append(examen)

Page 13: Programacion en python_3

Ejemplo práctico: universidad.pyclass AlumnoRegular(Alumno):

def rendir(self, materia, nota, fecha): if self.buscar_cursadas(materia): Alumno.rendir(self, materia, nota, fecha) else: raise RuntimeError("El alumno no curso la materia %s" % materia)

class AlumnoLibre(Alumno): "Los alumnos libres no necesitan inscribirse previamente"

pass

Page 14: Programacion en python_3

Ejemplo práctico: universidad.pyclass Universidad:

def __init__(self): self.alumnos = [] self.profesores = [] self.plan_estudio = [] self.cursos = []

def guardar(self, nombre_archivo="universidad.dat"): "Grabo la instancia en un archivo" archivo = open(nombre_archivo, "wb") # serializo el objeto universidad (max.protocolo) pickle.dump(universidad, archivo, -1)

@staticmethod def cargar(nombre_archivo="universidad.dat"): "Cargg la instancia desde un archivo" archivo = open(nombre_archivo, "rb") return pickle.load(archivo)

Page 15: Programacion en python_3

Ejemplo práctico: universidad.py def analitico(self, alumno): "Muestro el estado de las materias del alumno" print "Notas para el alumno: %s" % alumno for materia in self.plan_estudio: cursada = alumno.buscar_cursadas(materia) and True or False # obtengo el último exámen rendido examenes = alumno.buscar_examenes(materia) if examenes: # extraigo la última nota y fecha de exámen nota = examenes[-1].nota fecha = examenes[-1].fecha else: nota = fecha = '' print "%3s: %-20s %5s %10s %s" % ( materia.codigo, materia.nombre, nota, fecha, cursada and 'Cursada' or '') print

Page 16: Programacion en python_3

Ejemplo práctico: universidad.py def recibido(self, alumno): "Reviso que todas las materias del plan de estudio esten aprobadas" for materia in self.plan_estudio: # busco los examenes aprobados de esta materia if not [examen for examen in alumno.buscar_examenes(materia) if examen.aprobado]: raise RuntimeError("%s no aprobo %s" % (alumno, materia)) #return False # no aprobó ningún/no tiene exámen return True # aprobó todas las meterias

Page 17: Programacion en python_3

Ejemplo práctico: universidad.pyif __name__ == "__main__": # si existen los datos, los deserealizo if os.path.exists("universidad.dat"): universidad = Universidad.cargar() print "Cursos:" pprint.pprint(universidad.cursos) print "Alumnos" pprint.pprint(universidad.alumnos) else: universidad = Universidad() alumno1 = AlumnoLibre(dni=1234, nombre="Juan", apellido="Perez", nro_matricula=1234) alumno2 = AlumnoRegular(dni=5678, nombre="Luis", apellido="Gonzales", nro_matricula=1234) universidad.alumnos.extend([alumno1, alumno2]) ... universidad.guardar()

Page 18: Programacion en python_3

Ejemplo práctico: universidad.py>>> ================================ RESTART >>> Notas para el alumno: Juan, Perez 1: Bases de datos 5 2009-01-03 Cursada 2: Programacion I 3 2009-02-03 Cursada 3: Programacion II 7 2009-02-03

Notas para el alumno: Luis, Gonzales 1: Bases de datos 7 2009-01-03 Cursada 2: Programacion I 3: Programacion II 10 2009-02-03 Cursada

Juan, Perez Juan, Perez no aprobo Programacion I (2)Luis, Gonzales Luis, Gonzales no aprobo Programacion I (2)>>> alumno1.rendir(materia2, nota=4, fecha=datetime.date.today())>>> universidad.guardar()

Page 19: Programacion en python_3

Ejemplo práctico: universidad.py>>> ================================ RESTART >>> alumno1

Traceback (most recent call last): File "<pyshell#9>", line 1, in <module> alumno1NameError: name 'alumno1' is not defined>>> alumno1=universidad.alumnos[0]>>> universidad.recibido(alumno1)True>>> universidad.analitico(alumno1)Notas para el alumno: Juan, Perez 1: Bases de datos 5 2009-01-03 Cursada 2: Programacion I 4 2009-08-20 Cursada 3: Programacion II 7 2009-02-03

>>>

Page 20: Programacion en python_3

Ejemplo práctico: universidad.py>>> ================================ RESTART >>> Notas para el alumno: Juan, Perez 1: Bases de datos 5 2009-01-03 Cursada 2: Programacion I 3 2009-02-03 Cursada 3: Programacion II 7 2009-02-03

Notas para el alumno: Luis, Gonzales 1: Bases de datos 7 2009-01-03 Cursada 2: Programacion I 3: Programacion II 10 2009-02-03 Cursada

Juan, Perez Juan, Perez no aprobo Programacion I (2)Luis, Gonzales Luis, Gonzales no aprobo Programacion I (2)>>> alumno1.rendir(materia2, nota=4, fecha=datetime.date.today())>>> universidad.guardar()

Page 21: Programacion en python_3

DB-API: Interfase Estándar de BB.DD.

�Funciones y atributos del Módulo: �connect(parametros...): crea una conexiónparamstyle: formato de los parámetros

'qmark': ej. '...WHERE name=?''numeric': ej. '...WHERE name=:1''named': ej. '...WHERE name=:name''format': ej. '...WHERE name=%s''pyformat': ej. '...WHERE name=%(name)s'

ExcepcionesTipos de datos avanzados

Page 22: Programacion en python_3

DB-API: Interfase Estándar de BB.DD.

�Funciones y atributos del Módulo: Excepciones�Warning: advertencia (ej. texto truncado)Error: clase base de erroresInterfaseError: error con la comunicaciónDatabaseError: error con la base en siOperationalError: error en "inesperado"IntegrityError: integridad (PK, FK, etc.)InternalError: error interno de la base de datosProgrammingError: error en el SQLNotSupportedError: funcionalidad no soportada

Page 23: Programacion en python_3

DB-API: Interfase Estándar de BB.DD.

�Funciones y atributos del Módulo: Tipos/constructores

�Date(año,mes,día): fechaTime(hora,minuto,segundo): horaTimestamp(año,mes,día,hora,minuto,segundo): fecha y horaBinarycasdena): objetos grandes (blobs)STRING: tipo para cadenasBINARY: tipo para bloblsNUMBER: numerosDATETIME: fechas y/o horasROWID: identificador de filas

Page 24: Programacion en python_3

DB-API: Interfase Estándar de BB.DD.

�Objetos Conexión:.close(): cierra la conexión (.rollback si hay datos pendientes).commit(): confirma transacción pendiente.rollback(): deshace una transacción pendiente.cursor(): crea un objeto Cursor

Page 25: Programacion en python_3

DB-API: Interfase Estándar de BB.DD.

�Objetos Cursor:.execute(sql, params): ejecuta una consulta sql aplicando los parámetros params.description: secuencia de datos de los campos: (name, type_code, display_size, internal_size, precision, scale, null_ok).rowcount: cantidad de filas devueltas o afectadas.fechone(): devuelve un registro.fechall(): devuelve todos los registros.close(): cierra la conexión

Page 26: Programacion en python_3

DB-API: Interfase Estándar de BB.DD.

�Pasos:1. Importar el conector2. Conectarse a la base de datos (función connect del

módulo conector)3. Abrir un Cursor (método cursor de la conexión)4. Ejecutar una consulta (método execute del cursor)5. Obtener los datos (método fetch o iterar sobre el

cursor)6. Cerrar el cursor (método close del cursor)

Page 27: Programacion en python_3

DB-API: Ejemplo SqLiteimport sqlite3

con = sqlite3.connect(":memory:")cur = con.cursor()cur.execute(""" CREATE TABLE persona (nombre, apellido, edad)""")con.commit()cur.execute(""" INSERT INTO persona VALUES ('Juan', 'Perez', 25);""")cur.execute(""" INSERT INTO persona VALUES ('Nilda', 'Rodriguez', 25);""")con.commit()cur.execute("SELECT nombre, apellido, edad FROM persona")for nombre, apellido, edad in cur: print nombre, apellido, edad

Page 28: Programacion en python_3

DB-API: Ejemplo SqLiteimport sqlite3

con = sqlite3.connect("mydb")# acceder por nombre a las filas del cursor:con.row_factory = sqlite3.Row

cur = con.cursor()cur.execute("select apellido, edad from persona")for row in cur: assert row[0] == row["apellido"] assert row["apellido"] == row["aPeLLido"] assert row[1] == row["edad"] assert row[1] == row["EdaD"]

Page 29: Programacion en python_3

DB-API: Ejemplo SqLiteimport sqlite3

con = sqlite3.connect(":memory:")con.execute("create table persona (id integer primary key, nombre varchar unique)")

# con.commit() se llama automáticamente si ok (con with)with con: con.execute("insert into persona(nombre) values (?)", ("Juan",))

# con.rollback() se llama si hay excepción:try: with con: con.execute("insert into persona(firstname) values (?)", ("Juan",))except sqlite3.IntegrityError: print "no se puede agregar Juan dos veces"

Page 30: Programacion en python_3

DB-API: Ejemplo MySql>>> import MySQLdb

>>> db = MySQLdb.connect(host="localhost", user="root",... passwd="mypassword", db="PythonU")>>> cursor = db.cursor()cursor.execute("SELECT * FROM Students")

>>> cursor.fetchone()(1L, 'Joe', 'Campbell', datetime.date(2006, 2, 10), 'N')

>>> cursor.fetchall()((1L, 'Joe', 'Campbell', datetime.date(2006, 2, 10), 'N'),(2L, 'Joe', 'Doe', datetime.date(2004, 2, 16), 'N'),(3L, 'Rick', 'Hunter', datetime.date(2005, 3, 20), 'N'),(4L, 'Laura', 'Ingalls', datetime.date(2001, 3, 15), 'Y'),(5L, 'Virginia', 'Gonzalez', datetime.date(2003, 4, 2), 'N'))

Page 31: Programacion en python_3

DB-API: Ejemplo PostgreSQL>>> import psycopg2

>>> conn = psycopg2.connect(database='test', user='postgres', password='pass', host='localhost')

>>> # Crear un cursor para obtener y ejecutar consulta:>>> cur = conn.cursor()>>> cur.execute("SELECT * FROM estudiante")>>> rows=cur.fetchall()>>> print rows

[['Joe', 'Capbell', datetime.date(2006, 2, 10), False, 1], ['Joe', 'Doe', datetime.date(2004, 2, 16), False, 2], ['Rick', 'Hunter', datetime.date(2005, 3, 20), False, 3], ['Laura', 'Ingalls', datetime.date(2001, 3, 15), True, 4], ['Virginia', 'Gonzalez', datetime.date(2003, 4, 2), False, 5]]

Page 32: Programacion en python_3

DB-API: Ejemplo PostgreSQL>>> import psycopg2.extras>>> cur = conn.cursor(cursor_factory = psycopg2.extras.DictCursor) >>> cur.execute("SELECT * FROM estudiante")>>> for row in cur: # itero sober cada fila>>> # row es un diccionario, con las claves = campos>>> print "Nombre y Apellido: %s, %s " % (row['nombre'],row['apellido']) Nombre y Apellido: Joe, Capbell Nombre y Apellido: Joe, Doe Nombre y Apellido: Rick, Hunter Nombre y Apellido: Laura, Ingalls Nombre y Apellido: Virginia, Gonzalez

>>> print cur.description(('nombre', 1043, 8, -1, None, None, None), ('apellido', 1043, 8, -1, None, None, None), ('fecha', 1082, 10, 4, None, None, None), ('booleano', 16, 1, 1, None, None, None), ('legajo', 23, 1, 4, None, None, None))

Page 33: Programacion en python_3

DB-API: Ejemplo PostgreSQL>>> #Parámetros:>>> cur = conn.cursor()>>> cur.execute("insert into personas (apellido) values (%s)" , ["D'agostino"])

>>> cur.execute("insert into personas (apellido) values (%(apellido)s)" , {"apellido":"D'agostino"})

Page 34: Programacion en python_3

PlPython: Procedimientos almacenadosEl lenguaje procedural plpython permite escribir funciones python para la base de datos relacional PostgreSQL.El cuerpo de una funcion plpythonu es simplemente un script de Python.Los argumentos son pasados como elementos de una lista args; los argumentos por nombre son pasados como variables ordinarias. El resultado es devuelto con un return o un yieldLos valores NULL equivalen a None.Diccionario SD y GD (global)

Page 35: Programacion en python_3

PlPython: Procedimientos almacenadosEjemplo Simple:>>> CREATE FUNCTION pymax (a integer, b integer) RETURNS integerAS $$ if (a is None) or (b is None): return None if a > b: return a return b$$ LANGUAGE plpythonu;

-- invoco la función:SELECT pymax(2, 3);-- devuelve 3

Page 36: Programacion en python_3

PlPython: Procedimientos almacenadosRecibir tipos compuestos:CREATE TABLE empleado ( nombre TEXT, salario INTEGER, edad INTEGER);

CREATE FUNCTION sueldo_alto (e empleado) RETURNS booleanAS $$ if e["salario"] > 200000: return True if (e["edad"] < 30) and (e["salario"] > 100000): return True return False$$ LANGUAGE plpythonu;

Page 37: Programacion en python_3

PlPython: Procedimientos almacenadosDevolver tipos compuestos:CREATE FUNCTION crear_persona (nombre TEXT, apellido TEXT) RETURNS personaAS $$ return [ nombre, apellido ] # o como tupla: return ( nombre, apellido ) # o como diccionario: return { "nombre": nombre, "apellido": apellido } # return Persona(nombre, apellido)$$ LANGUAGE plpythonu;

Page 38: Programacion en python_3

PlPython: Procedimientos almacenadosDevolver multiples tipos escalares o compuestos:CREATE TYPE saludo AS ( mensaje TEXT, -- hola a_quien TEXT -- mundo);CREATE FUNCTION saludar (mensaje TEXT) RETURNS SETOF saludoAS $$ # devolver una tupla conteniendo lista de tipos # todas las otras combinaciones son posibles return ( [ mensaje, "Mundo" ], [ mensaje, "PostgreSQL" ], [ mensaje, "PL/Python" ] )

##for a_quien in [ "Mundo","PostgreSQL","PL/Python"]: ## yield ( mensaje, a_quien )

$$ LANGUAGE plpythonu;

Page 39: Programacion en python_3

PlPython: Procedimientos almacenadosDisparadores (triggers), recibe diccionario TD:

TD["new"]: valores nuevos de la fila afectada TD["old"]: valores viejos de la fila afectada TD["event"]: tipo de evento "INSERT", "UPDATE", "DELETE", o "UNKNOWN"TD["when"]: momento en que se ejecutó: "BEFORE" (antes del commit), "AFTER" (despues del commit), o "UNKNOWN"TD["args"]: argumentos adicionalesSi TD["when"] es BEFORE: se puede devolver None or "OK", "SKIP o "MODIFY"

Page 40: Programacion en python_3

PlPython: Procedimientos almacenadosAcceso a la base de datos: módulo plpy

plpy.error y plpy.fatal disparan una excepción python, si no se controla, se propaga y causa que la transacción se aborte. Equivalente a llamar raise plpy.ERROR(msg) y raise plpy.FATAL(msg)plpy.debug(msg), plpy.log(msg), plpy.info(msg), plpy.notice(msg), plpy.warning(msg) informan mensajes de distinta prioridadplpy.execute(sql, limite): ejecutar consultasplpy.prepare(sql, [tipos]): preparar consultas

Page 41: Programacion en python_3

PlPython: Procedimientos almacenadosEjecutar consulta con execute:

se obtienen las filas recorriendo el resultado por numero de fila y nombre de columnanrows: devuelve cantidad de filasstatus: estado interno

rv = plpy.execute("SELECT * FROM mi_tabla", 5)for fila in rv: print fila['columna']

Page 42: Programacion en python_3

PlPython: Procedimientos almacenadosPreparar y ejecutar consulta con execute:# preparo el planplan = plpy.prepare("SELECT apellido FROM usuario WHERE nombre = $1 AND casado = $2 ", [ "text", "boolean" ])

# ejecuto el plan con los parámetrosrv = plpy.execute(plan, [ "Mariano", True ], 5)

CREATE FUNCTION usar_plan() RETURNS trigger AS $$ if SD.has_key("plan"): plan = SD["plan"] # está el plan, lo reutilizo else: # no esta el plan, lo creo y almaceno plan = plpy.prepare("SELECT 1") SD["plan"] = plan # continua la función...$$ LANGUAGE plpythonu;

Page 43: Programacion en python_3

PlPython: Procedimientos almacenadosPreparar y ejecutar consulta con execute:# preparo el planplan = plpy.prepare("SELECT apellido FROM usuario WHERE nombre = $1 AND casado = $2 ", [ "text", "boolean" ])

# ejecuto el plan con los parámetrosrv = plpy.execute(plan, [ "Mariano", True ], 5)

CREATE FUNCTION usar_plan() RETURNS trigger AS $$ if SD.has_key("plan"): plan = SD["plan"] # está el plan, lo reutilizo else: # no esta el plan, lo creo y almaceno plan = plpy.prepare("SELECT 1") SD["plan"] = plan # continua la función...$$ LANGUAGE plpythonu;

Page 44: Programacion en python_3

PlPython: Procedimientos almacenadosEjemplo: analizador de direccionesCREATE TYPE direccion AS ( calle1 TEXT, calle2 TEXT, altura INTEGER);CREATE OR REPLACE FUNCTION analizar_dir(dir text) RETURNS direccion AS$BODY$ def is_numeric(x): return not [y for y in x if not '0'<=x<='9'] ... return nombres[0], nombres[1], altura and int(altura[-6:]) or None$BODY$ LANGUAGE 'plpythonu' IMMUTABLE;

select calle1, calle2, altura from analizar_dir('balcarce 50 esquina rivadavia')

Page 45: Programacion en python_3

Documentación y Ayuda

Documentación Oficial: http://docs.python.org/Libro Python para todosPython Argentina: Aprendiendo Python