Megachuletario para uso personal
(basado principalmente en el PDF "Python para todos")
Tipos
Python es de tipado dinámico (no se declaran las variables, el
tipo lo toman de aquello que se les asigna y pueden cambiar de tipo)
pero fuertemente tipado (las conversiones de tipo deben ser
explícitas).
Hola mundo (python "hola_mundo.py"):
#!/usr/bin/env python
# coding: utf-8
mi_variable = "Hola mundo"
print type(mi_variable)
El tipo int de Python se implementa a bajo nivel mediante un tipo
long de C. En la mayor parte de los casos, 32 bits, es decir, de
-2^31 a 2^31 – 1 (-2.147.483.648 a 2.147.483.647). En plataformas
de 64 bits, el rango es de -9.223.372.036.854.775.808 hasta
9.223.372.036.854.775.807.
El tipo long de Python permite almacenar números de cualquier
precisión, estando limitados solo por la memoria disponible en la
máquina.
Al asignar un número a una variable esta pasará a tener tipo
int, a menos que el número sea tan grande como para requerir el uso
del tipo long.
Long explícito: 23L
Octal explícito: 027
Hexadecimal explícito: 0x17
Números reales: 0.0001 y 0.1e-3
Operadores de conversión explícitos: float(), long(), bool(),
int(), complex().
None: equivalente a Null de otros lenguajes.
Operadores
Asignación:
a = b = 2
print a
print b
print a == b
Exponente:
2
** 8
División entera:
7
// 2
Módulo:
7 %
2
Aunque usemos el operador de división no entera (/), si ambos
operandos son enteros, el resultado también lo será. Por tanto para
asegurar que el resultado tenga decimales debemos asegurar que al
menos uno de los operandos sea real. P.e.:
3.0
/ 2 o bien
float(3)/2.
Operadores
binarios: & | ^ ~ << >>
'una' "dos" 'tres\ncuatro' # Cadenas de texto
u'eñe' # Unicode
r'\n' # Raw (no se hace sustitución de caracteres)
'''Primera
Segunda''' # Cadena en varias líneas
'hola' + ' ' +
'mundo' # Concatenación de cadenas
'.'*10 # Repetición de cadenas
c = 'hola mundo'
c[0] # h
c[-2] # d
c[5:7] # mu
c[2::3] # lmd
c[:3] # 3 primeros caracteres
c[-3:] # 3 últimos caracteres
Booleanos y relacionales:
True, False, and, or, not, ==, !=, <, >, <=, >=.
Colecciones
Listas
l = [22, True, 'hola', [11, 33]]
l[3][0] # 11
len(l) # 4
l[-1] = 'adiós' # sustituye [11, 33] por 'adiós'
l[0:2] = ['Uno por dos'] # l vale ['Uno por dos', 'hola', 'adiós']
l = [] # lista vacía
Tuplas (listas inmutables y más ligeras)
l = (1,) # la coma final diferencia la tupla de un entero entre paréntesis
type(l) #
l = (1) # no es una tupla
type(l) #
l = () # tupla vacía
Conjuntos
Un conjunto es una colección no ordenada y sin elementos
repetidos :
>>> canasta = ['manzana', 'naranja', 'manzana', 'pera', 'naranja', 'banana']
>>> fruta = set(canasta) # crea un conjunto sin repetidos
>>> fruta
set(['pera', 'manzana', 'banana', 'naranja'])
>>> 'naranja' in fruta # verificación de pertenencia rápida
True
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # letras únicas en a
set(['a', 'r', 'b', 'c', 'd'])
>>> a – b # letras en a pero no en b
set(['r', 'b', 'd'])
>>> a | b # letras en a o en b
set(['a', 'c', 'b', 'd', 'm', 'l', 'r', 'z'])
>>> a & b # letras en a y en b
set(['a', 'c'])
>>> a ^ b # letras en a o b pero no en ambos
set(['b', 'd', 'm', 'l', 'r', 'z'])
Diccionarios
Las claves tienen que ser inmutables (números, cadenas,
booleanos, tuplas... ).
d = {'Clave 1': 'Valor 1',
'Clave 2': 'Valor 2',
'Clave 3': 'Valor 3'}
d['Clave 2'] = 'Nuevo Valor 2'
d['Clave 4'] = 'Valor de la nueva clave'
'Clave 3' in d # True
len(d) # 3
Control de flujo
if numero < 0:
print “Negativo”
elif numero > 0:
print “Positivo”
print '...aunque si numero es una cadena también entra aquí...'
else:
print “Cero”
var = “par” if (num % 2 == 0) else “impar” # A if C else B
Si la rama de ejecución tiene una solo
línea se puede escribir de forma compacta:
if numero < 0: print “Negativo”
else: print “Positivo o Cero”
No existe estructura como 'switch' o
'select...case'.
Bucles
i = 0
while i < 5:
entrada = raw_input("> ")
if entrada == "fin":
break # sale del bucle 'while' o 'for' más interior
elif entrada == "paso":
continue # fuerza la siguiente iteración sin esperar a que finalice ésta
else:
print entrada
i += 1
else:
print 'salimos del bucle porque van 5 entradas; si saliésemos con "break" no se ejecutaría esto'
lista = ['uno', 'dos', 'tres']
for elemento in lista:
print elemento
for i in range(0, 5, 2):
print i # 0 2 4
if (i % 2) > 0: break # acabamos en cuanto encontremos el primer impar
else:
print 'todos los números eran pares'
Funciones
Número variable de argumentos mediante una tupla:
def mi_funcion2(param1, param2 = 'por defecto', *otros):
print param1
print param2
for val in otros:
print val
def mi_funcion3():
pass # definimos la función vacía (p.e. Para rellenarla más tarde)
mi_funcion2(1)
mi_funcion2(1, 2, 3, 'cuatro', 5)
Número variable de argumentos mediante un diccionario:
def varios(param1, param2, **otros):
for i in otros.items():
print i
varios(1, 2, tercero = 3, cuarto = 'cuatro')
En Python se utiliza el paso por valor de referencias a objetos ,
aunque los valores inmutables (enteros, tuplas...) se comportan como
paso por valor.
El ejemplo anterior es como un procedimiento, y éste es como una
función que devuelve una tupla:
def f(x, y):
return x * 2, y * 2
a, b = f(2, 3)
Orientación a Objetos
Clases y objetos
class Coche:
“””Abstraccion de los objetos coche.”””
def __init__(self, gasolina):
self.gasolina = gasolina
print “Tenemos”, self.gasolina, “litros”
def conducir(self):
if self.gasolina > 0:
self.gasolina -= 1
print “Quedan”, self.gasolina, “litros”
else:
print “No se mueve”
mi_coche = Coche(3)
mi_coche.conducir()
Clase como struct de C:
class Estructura:
pass # la definimos vacía
mio = Estructura()
mio.atributo1 = 'S.O. GNU'
mio.atributo2 = 'FLOSS'
Herencia
class Terrestre:
def desplazar(self):
print 'El animal anda'
def respirar(self):
print 'Respira fuera del agua'
class Acuatico:
def desplazar(self):
print 'El animal nada'
def respirar(self):
print 'Respira bajo el agua'
class Cocodrilo(Terrestre, Acuatico): #heredamos de ambas pero prevalece 'Terrestre'
def desplazar(self): #sobreescrimos los métodos heredados
print "Repta"
c = Cocodrilo()
c.desplazar()
Acuatico.desplazar(c) #usamos el método de una de la superclases de las que hereda
c.respirar()
Encapsulación
class Ejemplo:
def publico(self):
print 'Publico'
def __privado(self):
print 'Privado'
ej = Ejemplo()
ej.publico()
ej.__privado() # da error
ej._Ejemplo__privado() # truco para acceder al método "oculto"
class Fecha():
def __init__(self):
self.__dia = 1
def getDia(self):
return self.__dia
def setDia(self, dia):
if dia >= 1 and dia <= 31:
self.__dia = dia
else:
print 'No es un día'
mi_fecha = Fecha()
print mi_fecha.getDia()
mi_fecha.setDia(3)
mi_fecha.setDia(33)
print mi_fecha.getDia()
class Fecha2(object): # object es un objeto vacío de nuevo-estilo necesario para 'property'
def __init__(self):
self.__dia = 1
def getDia(self):
return self.__dia
def setDia(self, dia):
if dia >= 1 and dia <= 31:
self.__dia = dia
else:
print 'No es un día'
dia = property(getDia, setDia)
mi_fecha2 = Fecha2()
print mi_fecha2.dia
mi_fecha2.dia = 3
mi_fecha2.dia = 33
print mi_fecha2.dia
Métodos especiales
__init__(self, args)
__new__(cls, args)
__del__(self)
__str__(self)
__cmp__(self, otro)
__len__(self)
Métodos de objetos
Diccionario.get(k[, d])
Diccionario.has_key(k)
Diccionario.items()
Diccionario.keys()
Diccionario.pop(k[, d])
Diccionario.values()
Cadena.count(sub[, start[, end]])
Cadena.find(sub[, start[, end]])
Cadena.join(sequence)
Cadena.partition(sep)
Cadena.replace(old, new[, count])
Cadena.split([sep [,maxsplit]])
Cadena.lower()
Cadena.upper()
Cadena.swapcase()
Cadena.title()
Cadena.strip()
Lista.append(object)
Lista.count(value)
Lista.extend(iterable)
Lista.index(value[, start[, stop]])
Lista.insert(index, object)
Lista.pop([index])
Lista.remove(value)
Lista.reverse()
Lista.sort(cmp=None, key=None, reverse=False)
Funciones de orden superior
def funcion():
print 'Hola'
f = funcion
f()
Iteraciones de orden superior sobre listas
def cuadrado(n):
return n ** 2
l = [1, 2, 3]
print map(cuadrado, l)
def es_par(n):
return (n % 2.0 == 0)
l = [1, 2, 3]
print filter(es_par, l)
l = [1, 2, 3]
print filter(lambda n: n % 2.0 == 0, l)
def sumar(x, y):
return x + y
l = [1, 2, 3]
print reduce(sumar, l)
Comprensión de listas
Se aconsejan en lugar de 'map' y 'filter'.
l = [0, 1, 2, 3]
l2 = [n ** 2 for n in l]
l2 = [n for n in l if n % 2.0 == 0]
m = ['a', 'b']
n = [s * v for s in m
for v in l
if v > 0]
Generadores
Función que genera valores sobre los que iterar . Son como listas
pero en lugar de estar completas en memoria, se generan poco a poco.
Decoradores
def mi_decorador(funcion):
def nueva(*args):
print 'Llamada a la funcion', funcion.__name__
retorno = funcion(*args)
return retorno
return nueva
Así todas las llamadas a 'imprime' son "decoradas":
@mi_decorador
def imprime(texto):
print texto
imprime('Hola')
Así solo se decoran las llamadas deseadas:
def imprime(texto):
print texto
mi_decorador(imprime)('Hola')
Excepciones
a = 1
b = 0
try:
r = a / b
except (NameError, ValueError):
print 'La variable no existe o el valor no es un número'
except:
print 'otro tipo de error'
else:
print 'sin errores'
finally:
print 'Se ejecuta siempre al final de todo'
Otros resultados cambiando 'b=0' por 'c=0' y por 'b=1'.
También podemos definir y lanzar nuestras propias excepciones:
class MiError(Exception):
def __init__(self, valor):
self.valor = valor
def __str__(self):
return 'Error ' + str(self.valor)
resultado = 22
try:
if resultado > 20:
raise MiError(33)
except MiError, e:
print e
Lista de excepciones por defecto:
BaseException, Exception, GeneratorExit, StandardError, ArithmeticError, FloatingPointError, OverflowError, ZeroDivisionError, AssertionError, AttributeError, EOFError, EnvironmentError, IOError, OSError, WindowsError, ImportError, LookupError, IndexError, KeyError, MemoryError, NameError, UnboundLocalError, ReferenceError, RuntimeError, NotImplementedError, SyntaxError, IndentationError, TabError, SystemError, TypeError, ValueError, UnicodeError, UnicodeDecodeError, UnicodeEncodeError, UnicodeTranslateError, StopIteration, Warning, DeprecationWarning, FutureWarning, ImportWarning, PendingDeprecationWarning, RuntimeWarning, SyntaxWarning, UnicodeWarning, UserWarning, KeyboardInterrupt, SystemExit.
Módulos y Paquetes
Módulos
import time
print time.asctime()
from time import asctime
print asctime()
if __name__ == “__main__”:
print “Se muestra si no es importacion sino que lo ejecutamos directamente”
Paquetes
Un paquete es un directorio que contiene varios módulos, que son
archivos. Asimismo debe contener un archivo llamado __init__.py:
import paq.subpaq.modulo
paq.subpaq.modulo.func()
Entrada/Salida Y Ficheros
Entrada y salida
try:
edad = raw_input(“Cuantos anyos tienes? “)
dias = int(edad) * 365
#print 'Has vivido ' + dias + ' dias' # incorrecto
print 'Has vivido ' + str(dias) + ' dias' # correcto
print 'Has vivido', dias, 'dias' # correcto
print 'Línea 1.',
print 'Sigo en la línea 1.\tTabulado.'
except ValueError:
print “Eso no es un numero”
print 'adecuado para humanos:', str(0.1), ' ,, para intérprete:', repr(0.1)
import sys
for i in range(0, len(sys.argv)):
print sys.argv[i],
Formateo de cadenas:
from math import pi
# vieja manera
print '%8s %-18.16s %x %.4f' % ('Hola', 'Hexadecimal y Piiiii', 255, pi)
# nuevas maneras
print '{0} {1:18.16s} {2:x} {3:.4f}'.format('Hola'.rjust(8), 'Hexadecimal y Piiiii', 255, pi)
print '{0} {1:18.16s} {3:x} {2:.4f}'.format('Hola'.rjust(8), 'Hexadecimal y Piiiii', pi, 255)
print '{0} {1:18.16s} {hexa:x} {2:.4f}'.format('Hola'.rjust(8), 'Hexadecimal y Piiiii', pi, hexa=255)
Archivos
‘r’: read. El archivo tiene que existir previamente, sino
excepción de tipo IOError.
‘w’: write. Si el archivo no existe se crea. Si existe,
sobreescribe el contenido.
‘a’: append. Se comienza a escribir al final del archivo.
‘b’: binary, binario.
‘+’: permite lectura y escritura simultáneas.
‘U’: universal newline .
Métodos: read, readline, readlines, write, writelines, seek,
tell.
f = open('archivo.txt', 'w')
f.writelines('línea 1\n')
f.close()
f = open('archivo.txt', 'a')
f.write('lín')
f.writelines('ea 2\n')
f.close()
f = open('archivo.txt', 'r')
print f.read(5) # bytes (no caracteres)
print f.tell()
f.seek(-1, 2) # desde el final
print f.tell()
f.seek(4, 0) # desde el principio (por defecto)
print f.tell()
f.seek(-1, 1) # desde la posición actual
print f.tell()
l = f.readlines()
for e in l: print e,
f.close()
Fichero temporal que se elimina automáticamente al cerrarlo:
tempfile.TemporaryFile()
Expresiones Regulares
import re
if re.match('(c|p|j)yth..', 'python'): print 'coincide'
if re.match('\.config', '.config'): print 'coincide'
if re.match('\.config|\.setup', '.config'): print 'coincide'
if re.match('[pjc]yt[a-zA-Z]on[^a-zA-Z][.,]', 'python2.'): print 'coincide'
if re.match('^http', 'http://mundogeek.net'): print 'coincide'
'\d': Equivale a [0-9]
'\D': Equivale a [^0-9]
'\w': Equivale a [a-zA-Z0-9_]
'\W': Equivale a [^a-zA-Z0-9_]
'\s': Equivale a [ \t\n\r\f\v]
'\S': Equivale a [^ \t\n\r\f\v]
Lo que tenemos a la izquierda...
'+': puede aparecer una o mas veces.
'*': puede aparecer cero o mas veces.
'?': puede aparecer cero o una vez.
'{3,8}': tiene que aparecer de 3 a 8 veces.
're.match' tiene un tercer parámetro opcional que puede tomar
como valor: 're.IGNORECASE ', 're.VERBOSE '. Devuelve 'None' o un
objeto de tipo MatchObject con métodos 'start', 'end', 'group' y
'groups '.
're.search' funciona de forma similar a 're.match ', aunque
buscamos cualquier parte de la cadena que se ajuste al patrón ,
mientras que con 're.match' la cadena debe ajustarse al patrón desde
el primer carácter de la cadena .
're.findall' devuelve una lista con las subcadenas que cumplieron
el patrón.
're.finditer' devuelve un iterador con el que consultar uno a uno
los distintos MatchObject.
're.split' y 're.sub' son como 'split' y 'sub' admitiendo
expresiones regulares. Para mejorar su rendimiento podemos crear un
objeto RegexObject con 're.compile'.
Sockets
Módulo 'socket '. Sockets de flujo 'socket.SOCK_STREAM' (TCP) y
sockets de datagramas 'socket.SOCK_DGRAM' (UDP). Familia
'socket.AF_INET', 'socket.AF_INET6'...
Métodos recv y send (en TCP) y recvfrom y sendfrom (en UDP) .
Servidor
import socket
socket_s = socket.socket()
socket_s.bind((“localhost”, 9999))
socket_s.listen(10) # máximo 10 conexiones
socket_c, (host_c, puerto_c) = socket_s.accept()
recibido = socket_c.recv(1024) # recogemos 1024 bytes como máximo
print “Recibido: “, recibido
socket_c.send(recibido)
socket_c.close()
socket_s.close()
Cliente
import socket
s = socket.socket()
s.connect((“localhost”, 9999))
s.send(mensaje)
s.close()
Interactuar con webs
import urllib, urllib2
try:
f = urllib2.urlopen('http://www.python.org')
#Modificamos UserAgent
o_request = urllib2.Request('http://www.python.org', headers={'User-Agent': 'Cliente Http Python'})
f = urllib2.urlopen(o_request)
params = urllib.urlencode({'usuario': 'manuel', 'password': 'contraseña'})
#Parámetros por POST
f = urllib2.urlopen('http://ejemplo.com/login', params)
#Parámetros por GET
f = urllib2.urlopen('http://ejemplo.com/login' + '?' + params)
print f.read()
f.close()
except:
print 'Ocurrió un error' # si no queremos hacer nada pondríamos 'pass'
Threads
En Python podemos crear nuevos procesos mediante la función os.
fork, o mediante otras funciones más avanzadas como popen2.popen2.
Sin embargo el cambio de contexto puede ser relativamente lento, y
los recursos necesarios, demasiados .
Los Threads (tb. llamados hilos o procesos ligeros ) son más
ligeros que los procesos, el cambio de contexto es más rápido y
dado que los threads comparten el mismo espacio de memoria global,
cualquier variable global que tengamos en nuestro programa es vista
por todos los threads.
import threading, time, random
class MiThread(threading.Thread):
def __init__(self, num, nombre):
threading.Thread.__init__(self)
self.num = num # atributo creado por nosotros
self.name = nombre # atributo existente en la clase (por defecto 'Thread-1'...)
def run(self):
print 'Soy el hilo nº', self.num, self.name
time.sleep(random.random() * 0.3) # esperamos entre >= 0 y < 0.3
print 'Soy el hilo principal'
for i in range(0, 10):
t = MiThread(i, 'mi_hilo' + str(i))
t.start()
t.join() # espera x seg. a que finalice la ejecución del hilo
Sincronización
Para sincronizar el acceso a ciertos recursos por parte de los
threads tenemos mecanismos de sincronización : locks (también
llamados mutex, cierres de exclusión mutua o candados), locks
reentrantes y semáforos.
lista = []
lock = threading.Lock()
def anhadir(obj):
lock.acquire() # se queda bloqueado aquí hasta que el candado esté libre
lista.append(obj)
lock.release()
def obtener():
# indicamos con False que no queremos esperar
# si devuelve True es que nos lo han dado (estaba libre)
if lock.acquire(False) == True:
obj = lista.pop()
lock.release()
return obj
La clase RLock funciona de forma similar a Lock, pero en este caso
el candado puede ser adquirido por el mismo thread varias veces, y no
quedará liberado hasta que el thread llame a release tantas veces
como llamó a acquire.
Los semáforos son otra clase de candados en que el constructor de
Semaphore puede tomar como parámetro opcional el número máximo
inicial de threads que pueden acceder a la vez a la sección de
código crítico. La clase BoundedSemaphore impide que se supere el
número máximo inicial.
Otras formas de sincronizar son las Condiciones, los Eventos y el
uso de un decorador para hacer que sólo un thread pueda acceder al
método sobre el que se utiliza a la vez .
Datos globales independientes
Para ocultar las variables del hilo principal a los hilos hijo
utilizamos threading.local() donde creamos atributos para "ocultar"
datos.
Compartir información
Para compartir información entre los threads de forma sencilla
podemos utilizar la clase Queue.Queue . Nos ahorra tener que
sincronizar el acceso a los datos nosotros mismos. Los métodos
put(item) y get() tienen un parámetro booleano opcional block que
indica si queremos que se espere hasta que haya algún elemento
disponible y otro timeout que indica, en segundos, el tiempo máximo
a esperar. Con qsize obtenemos el tamaño de la cola y con empty() y
full() podemos comprobar si está vacía o llena.
q = Queue.Queue()
class MiThread(threading.Thread):
def __init__(self, q):
self.q = q
threading.Thread.__init__(self)
def run(self):
while True:
try:
obj = q.get(False)
except Queue.Empty:
print “Fin”
break
print obj
for i in range(10):
q.put(i)
t = MiThread(q)
t.start()
t.join()
Serialización de objetos
try:
import cPickle as pickle
except ImportError:
import pickle
fichero = file('datos.dat', 'w')
animales = ['pitón', 'mono', 'camello']
pickle.dump(animales, fichero, 1) # 0=texto (por defecto) ,, 1=binario
fichero.close()
fichero = file('datos.dat')
animales2 = pickle.load(fichero)
print animales2
fichero.close()
El módulo shelve extiende pickle / cPickle para acceder a la
versión serializada de un objeto mediante una cadena asociada, a
través de una estructura parecida a un diccionario. Como un
diccionario cualquiera la clase Shelf cuenta con métodos get,
has_key, items, keys, values...
Bases de Datos
Python incorpora un módulo compatible con esta base de datos que
sigue la especificación de DB API 2.0: sqlite3.
La sintaxis a utilizar para insertar valores en la consulta SQL de
forma dinámica es qmark:
sql = “select all from t where valor=?”
Ejemplo:
import sqlite3 as dbapi
conn = dbapi.connect("bbdd.dat") # opcional :memory:
cursor = conn.cursor()
cursor.execute("drop table empleados")
cursor.execute("""create table empleados (dni text, nombre text, departamento text)""")
cursor.execute("""insert into empleados
values ('12345678-A', 'Manuel Gil', 'Contabilidad')""")
conn.commit() #no es necesario si tenemos autocommit pero no está de más para dejar el código preparado para otro caso/BDD
cursor.execute("""select * from empleados where departamento='Contabilidad'""")
for tupla in cursor: # tb. cursor.fetchone(), cursor.fetchmany(n) o cursor.fetchall() ; (si no se indica n, se toma cursor.arraysize que vale 1 por defecto)
print tupla
for t in [('87654321-Z', 'Pepe Mel', 'Informatica'),
('dni2', 'Yo', 'sistemas')]:
cursor.execute('insert into empleados values (?,?,?)', t)
# ¿esto es equivalente?
c.executemany('insert into empleados values (?,?,?)',
[
('87654321-Z', 'Pepe Mel', 'Informatica'),
('dni2', 'Yo', 'sistemas')
] )
conn.rollback()
t = ('Informatica',)
cursor.execute("""select * from empleados
where departamento=?""", t) # evitamos inyección de código
print cursor.fetchone() # Dará None por el rollback
cursor.close()
conn.close()
La API de bases de datos de Python incluye una serie de
constructores :
- Date(year, month, day): Para almacenar fechas.
- Time(hour, minute, second): Para almacenar horas.
- Timestamp(year, month, day, hour, minute, second): Para
almacenar timestamps (una fecha con su hora).
- DateFromTicks(ticks): Para crear una fecha a partir de un
número con los segundos transcurridos desde el epoch (el 1 de Enero
de 1970 a las 00:00:00 GMT).
- TimeFromTicks(ticks): Similar al anterior, para horas en
lugar de fechas.
- TimestampFromTicks(ticks): Similar al anterior, para
timestamps.
- Binary(string): Valor binario.
Documentación
En Python si el primer estamento de la definición del objeto es
una cadena, esta se asocia a la variable __doc__ automáticamente.
def haz_algo(arg):
'''Esta función es para...'''
print arg # por poner algo
print haz_algo.__doc__
help(haz_algo)
Pruebas
Pruebas unitarias, pruebas de integración y pruebas de regresión.
La solución más extendida para las pruebas unitarias en el mundo
Python es unittest, a menudo combinado con doctest para pruebas más
sencillas.
Doctest
doctest permite combinar las pruebas con la documentación.
En 'modulo1.py':
def cuadrados(lista):
'''Calcula el cuadrado de los numeros de una lista
>>> l = [3, 2, 1, 0]
>>> cuadrados(l)
[9, 4, 1, 0]
>>> tirar = l.pop(3) # tenemos que asignar el valor a algo para que la prueba no finalice aquí
>>> l.append(4)
>>> cuadrados(l) == [9, 4, 1, 16]
True
'''
return [n ** 2 for n in lista]
if __name__ == '__main__':
import doctest
doctest.testmod() # se le puede indicar el módulo a evaluar (por defecto, el actual)
En 'modulo2.py':
def cuadrado(num):
'''Calcula el cuadrado de un numero.
>>> l = [0, 1, 2, 3]
>>> for n in l:
... cuadrado(n)
0
1
4
9
'''
return num ** 2
En 'validar.py':
import doctest, modulo1, modulo2
doctest.testmod(modulo1)
doctest.testmod(modulo2)
unittest / PyUnit
import unittest
def cuadrado(num):
'''Calcula el cuadrado de un numero.'''
return num ** 2
class EjemploPruebas(unittest.TestCase):
def test(self):
l = [0, 1, 2, 3]
r = [cuadrado(n) for n in l]
self.assertEqual(r, [0, 1, 4, 9])
if __name__ == '__main__':
unittest.main()
Consejos
No usar 'from module import *' e intentar no usar 'from module
import name1, name2'. En su lugar usar:
import module
module.name1...
Compare:
# ugh!
return dir+"/"+file
# better
return os.path.join(dir, file)
More useful functions in os.path: basename(), dirname() and
splitext().
No usar backslash para continuar instrucciones (un espacio añadido
al final puede cambiarlo todo); en su lugar encerrar todo entre
paréntesis y simplemente partir la línea en 2:
mal:
value = foo.bar()[’first’][0]*baz.quux(1, 2)[5:9] \
+ calculate_number(10, 20)*forbulate(500, 360)
bien:
value = (foo.bar()[’first’][0]*baz.quux(1, 2)[5:9]
+ calculate_number(10, 20)*forbulate(500, 360))
lxml
Conceptos básicos
from lxml import etree
Construyendo un XML:
raiz = etree.Element("root")
print raiz.tag # etiqueta XML
raiz.append(etree.Element("child1"))
hijo2 = etree.SubElement(raiz, "child2") # más eficiente que lo anterior
hijo2.set("id", "002")
print hijo2.get("id")
etree.SubElement(raiz, "child3", id="003", tipo="hijo") # podemos ignorar el valor devuelto e indicar atributos
print(etree.tostring(raiz, pretty_print=True)) # serializamos el XML
atributos = raiz[1].attrib # diccionario con los atributos
print atributos
Los elementos funcionan casi como listas Python:
print len(raiz) # nº de nodos hijos
raiz.append(etree.Element("child3")) # añade un nodo al final (aunque es más eficiente 'etree.SubElement()')
raiz.insert(0, etree.Element("child0")) # creamos un nuevo nodo en la primera posición
# los nodos no se pueden copiar, solo mover (a no ser que utilicemos deepcopy: from copy import deepcopy)
raiz.insert(1, raiz[2]) # esto mueve 'child2' antes que 'child1'
raiz[0] = raiz[-1] # esto mueve 'child3' a la primera posición "sobresscribendo" 'child0'
print [etree.tostring(n) for n in raiz]
Moverse por el árbol:
raiz[0].getparent() # raiz
raiz[1].getprevious() # raiz[0]
raiz[0].getnext() # raiz[1]
Texto en nodos:
nieto1 = etree.SubElement(hijo1, "grandchild") # ahora agregamos un nodo después del texto
nieto1.text = "texto"
nieto1.tail = "nieto de hijo1" # añade el texto después del nodo (no dentro)
hijo2 = raiz[1]
nieto2 = etree.SubElement(hijo2, "grandchild", descripcion="hijo de hijo2") # ahora agregamos un nodo después del texto
print etree.tostring(hijo1)
print hijo1.text # solo entrega el texto hasta que aparezca otra etiqueta
print etree.tostring(hijo1, method="text") # nos saltamos las etiquetas
Tree iteration:
print [nodo.tag for nodo in raiz.iter()] # todo el árbol
print [etree.tostring(nodo) for nodo in raiz[2].iter()] # solo la rama del tercer nodo
print [etree.tostring(nodo) for nodo in raiz.iter('grandchild')] # solo los nodos con esa etiqueta
Nodos especiales:
raiz.append(etree.Entity("#234"))
raiz.append(etree.Comment("comentario"))
print [nodo.text for nodo in raiz.iter(tag=etree.Entity)]
print [nodo.text for nodo in raiz.iter(tag=etree.Comment)]
ElementPath (en algunos sentidos hace lo mismo que iterar filtrando
por tag):
raiz.find('grandchild') # primer hijo directo con ese tag
raiz.find('.//grandchild') # primer descendiente con ese tag
raiz.find('grandchild[@descripcion]') # primer hijo directo con ese tag
raiz.findall('.//grandchild') # lista con todos los descendientes con ese tag
raiz.iterfind('.//grandchild') # iterador de todos los descendientes con ese tag
raiz.findtext('grandchild') # .text del primer hijo directo con ese tag
La clase ElementTree
Es como un documento que contiene, además de un nodo raíz,
comentarios, DOCTYPE... Por tanto si 'arbol' es de ese tipo (p.e.
porque es el parseado de un fichero), no existirá 'arbol.tag', sino
'arbol.getroot().tag' ya que el nodo raíz es solo una de las
posibles partes del árbol.
Parseado de cadenas
'fromstring()' y 'XML()' parsean una cadena de texto generando un
'Element' XML.
'parse()' parsea objetos tipo fichero y URLs generando un
'ElementTree' (no un Element). I.e. Genera un documento completo
incluyendo comentarios, DOCTYPE...
import StringIO
s = ' ]>&tasty;'
arbol = etree.parse(StringIO.StringIO(s))
print 'arbol:\n' + etree.tostring(arbol, pretty_print=True)
rama = etree.XML(s)
print 'rama:\n' + etree.tostring(rama, pretty_print=True)
Sustituyendo al parser por defecto:
parser = etree.XMLParser(remove_blank_text=True) # this creates a parser that removes empty text between tags
root = etree.XML(" ", parser)
Parseado dirigido por eventos:
some_file_like = StringIO.StringIO("data1data2data3")
for event, element in etree.iterparse(some_file_like):
print("%5s, %4s, %s" % (event, element.tag, element.text))
some_file_like = StringIO.StringIO("data1data2data3")
for event, element in etree.iterparse(some_file_like, events=("start", "end")):
print("%5s, %4s, %s" % (event, element.tag, element.text))
# para parsear un XML grande tipo DUMP
some_file_like = StringIO.StringIO("data1data2data3")
for _, element in etree.iterparse(some_file_like, tag="a"):
print("%4s, %s" % (element.tag, element.text))
element.clear()
ooolib
Es un proyecto abandonado y sustituido por odslib-python, pero
perfecto para leer y escribir hojas de cálculo OpenOffice (ODS).
# código que muestra como se lee y escribe un ODS llamado 'ejemplo.ods'
import ooolib
libro = ooolib.Calc()
libro.load('ejemplo.ods')
# recorremos todas las hojas
for i in range(0, libro.get_sheet_count()):
libro.set_sheet_index(i) # nos situamos en la hoja
print libro.get_sheet_name()
# obtenemos la esquina inferior derecha del rectángulo que contiene a los datos
(num_cols, num_filas) = libro.get_sheet_dimensions()
# escribimos un decimal en la primera columna justo debajo de la última fila
libro.set_cell_value(1, num_filas + 1, 'float', 3.14159)
num_filas += 1 # actualizamos el nº de filas
libro.save('ejemplo.ods') # grabamos a disco los cambios
for y in range(1, num_filas + 1):
for x in range(1, num_cols + 1):
# tupla con el tipo de dato (string, float...) y su valor
dato = libro.get_cell_value(x, y)
if dato == None:
print '\tNone' # no hay nada en la celda
else:
print '\ttipo:', dato[0], ',,', 'valor:', dato[1]
web2py
Es un framework para programación web. La mejor guía es el PDF
oficial (
http://www.web2py.com/book).
Lo que sigue son puntualizaciones mías o cosas recurrentes para mí.
Un
select()
devuelve un conjunto de registros (
Set
)
lleno o vacío (
len()=0
o not, pero no
None
). Si cogemos uno solo de ellos
mediante un iterador (o añadiendo
.first()
a
select()
) obtenemos un objeto
Row
o
None
.
Chuleta:
rows.as_list() # list of dictionaries
rows[0].as_dict() # row.as_dict()
rows3 = rows1 & rows2 # union
rows3 = rows1 | rows2 # union removing
duplicates
for row in rows.sort(lambda row:
row.name):