Archive for Python
July 11, 2009 at 17:38 · Filed under Python
Pensé que ya tenía una copia de The Little Book of Semaphores, pero por más que la buscaba no la encontraba.
Probablemente no la encontrara debido al enorme barullo que tengo en el directorio donde el aMule deposita todo lo que descarga y yo deposito todo lo que me bajo y quiero compartir.
Seguramente hubiera otro fichero con mismo contenido y distinto nombre. ¿Cómo encontrarlo?
Mediante md5sum *.pdf | sort podría revisar una por una cada entrada devuelta y buscar con un poco menos de dolor si hay algún duplicado, ya que sort ordena, pero que yo sepa no tiene una opción para mostrar sólo los duplicados (y en el manual no viene nada al respecto).
Quizá awk me ayudara, pero el caso es que hay que practicar Python (descargar fuente):
import sys
import hashlib
def do_hash(nombreFichero):
fichero=open(nombreFichero,‘r’)
contenido=fichero.read()
fichero.close()
return hashlib.md5(contenido).hexdigest()
if __name__==‘__main__’:
hashes={}
for fichero in sys.argv[1:]:
clave = do_hash(fichero)
try:
hashes[clave].append(fichero)
except KeyError:
hashes[clave]=[fichero]
for clave in hashes.keys():
if len(hashes[clave])>1:
print ‘Same key %s for:’%(clave)
for fichero in hashes[clave]:
print ‘\t%s’%(fichero)
Funcionamiento:
Una vez comprobado que el script se está ejecutando (__name__ == ‘__main__’), y no ha sido llamado como módulo, por cada fichero en la línea de comandos averigua su hash mediante la función do_hash (que internamente usa MD5, pero podría usar SHA1 sin más que cambiarlo).
En un diccionario se guarda por cada hash, una lista con los nombres de los ficheros que tienen la misma hash.
Posteriormente, por cada hash encontrada, si en la lista de ficheros que tienen esa hash hay más de un fichero, se imprime cada nombre de fichero, de manera que el usuario puede visualizar qué ficheros tienen el mismo contenido:
pablo@golgi:~/Desktop$ python dups.py *.pdf
Same key 3f4568dc0b3e96b94f02c0b58d57b702 for:
tls.2.pdf
tls.pdf
pablo@golgi:~/Desktop$ rm tls.2.pdf
Probablemente, una salida de este estilo (agrupando los ficheros iguales sin verborrea):
pablo
@golgi:~
/Desktop$ python dups.py
*.pdf
tls.2.pdf
tls.pdf
same1_1.pdf
same1_2.pdf
same1_3.pdf
pablo@golgi:~/Desktop$ rm tls.2.pdf same1_2.pdf same1_3.pdf
fuera más amigable para utilizar el comando desde algún script.
Ahí queda eso, por si a alguien (o a mí mismo en el futuro) le sirve para algo.
Ideas:
- hacerlo en bash, o al menos de una manera más simple.
- Si no es posible, programar una opción para ello en el sort.
- Me gustaría ver qué tal funciona la función do_hash() con ficheros enormes (de 1G., p.e.). Esto habría que arreglarlo…
dups.py
March 28, 2009 at 20:18 · Filed under Output, Python
Python tiene una característica que son las propiedades, pero al no conocerlas bien y ser particulares de este lenguaje, he creado una minúscula aplicación que genera el código para iniciar y acceder (get y set) a las propiedades de un objeto.
Esencialmente, es una función a la que le pasamos el nombre de las propiedades y su valor inicial, por ejemplo:
buildAccessor([
(‘configfile’,‘\’/etc/foo.conf\’‘),
])
Y esto nos genera el código que debemos incluir en el constructor, así como los métodos get y set:
self.__configfile =
‘/etc/foo.conf’
def getConfigfile(self):
return self.__configfile
def setConfigfile(self, value):
self.__configfile=value
Con lo que si hacemos una prueba:
class objectFoo:
def __init__(self):
self.__configfile =
‘/etc/foo.conf’
def getConfigfile(self):
return self.__configfile
def setConfigfile(self, value):
self.__configfile=value
if __name__==‘__main__’:
test=objectFoo()
print(‘test.getConfigfile() says \’‘ + test.getConfigfile() + ‘\’‘)
test.setConfigfile(‘/home/linz/.foo’)
print(‘Now, test.getConfigfile() says \’‘ + test.getConfigfile() + ‘\’‘)
Resulta esto:
pablo@golgi:~/Desktop/py-pwgen$ python test_buildAccessor.py
test.getConfigfile() says ‘/etc/foo.conf’
Now test.getConfigfile() says ‘/home/linz/.foo’
pablo@golgi:~/Desktop/py-pwgen$
He decidido ponerle una página estática, por que puede ser que en el futuro amplíe el trabajo, o incluso que lo suba un SVN.
January 3, 2009 at 00:12 · Filed under Python
Observemos el siguiente código:
# -*- coding: UTF-8 -*-
def foo(bar=False):
return ‘"bar" vale "%s"’%(bar)
if __name__==‘__main__’:
print (‘Cuando no se especifica el argumento, toma el valor por defecto:\n\tfoo() retorna ‘+foo())
print (‘Cuando sí se especifica el argumento, toma el valor especificado:\n\tfoo(bar=\’baz\’) retorna ‘+foo(bar=‘baz’))
La función foo está definida de manera que recibe un parámetro llamado bar. Tal y como está especificada, si al llamarla no se le pasa ese parámetro, bar tomará el valor por defecto False. Por ejemplo, con la llamada foo(), sin más, bar pasa a ser False, con lo que la función retorna “bar” vale “False”.
Si sí se le pasa ese argumento, la función simplemente usa el valor proporcionado:
>>> import wx_formas
>>> wx_formas.foo(3)
‘"bar" is "3"’
>>> wx_formas.foo(None)
‘"bar" is "None"’
>>> wx_formas.foo([1,2,3])
‘"bar" is "[1, 2, 3]"’
>>>
La ejecución del código original da el siguiente resultado:
Cuando no se especifica el argumento, toma el valor por defecto:
foo() retorna "bar" vale "False"
Cuando sí se especifica el argumento, toma el valor especificado:
foo(bar=‘baz’) retorna "bar" vale "baz"
También se le puede pasar un número variable de argumentos a una función, bien con def foo(*tupla_de_argumentos), o bien con def foo(**diccionario_de_argumentos).
En el libro Learning Python, de Mark Lutz, hay más información acerca de esto (capítulo 16, pág. 331 en la tercera edición, de octubre de 2007 en inglés).
Por darle un poco más de chicha al artículo, diré que acabo de encontrar la Py Zine, revista dedicada enteramente a Python.
December 6, 2008 at 23:53 · Filed under Python
Del libro Learning Python, de Mark Lutz. Ejercicios de la parte VII, acerca de las excepciones en Python.
El ejercicio 1, aunque están resueltos en el propio libro, pongo mi solución por aquí y la comento:
- Enunciado: Escribir una función llamada oops que lanza una excepción IndexError. Después, escribir otra función que llame a oops dentro de una estructura try/except para capturar la excepción. ¿Qué ocurre si oops lanzara la excepción KeyError? ¿De dónde vienen los nombres KeyError e IndexError? (Pista: recuerda que todos los identificadores no calificados vienen de uno de los cuatro ámbitos, según la regla LEGB)
- Mi solución:
def oops
(excepcion
):
raise excepcion
def caller():
try:
oops(KeyError)
except IndexError:
print("Received an IndexError exception")
except KeyError:
print("Received an KeyError exception")
if __name__=="__main__":
caller()
def oops(excepcion):
raise excepcion
Define una función llamada oops que recibe un único argumento referenciado por el identificador excepcion. Notar que no se define de qué tipo debe ser el argumento, si lo que hiciera nuestra función funcionara igual con enteros que con floats no tendríamos que programar dos funciones diferentes, nos valdría con la misma.
En el cuerpo de la función, raise lanza una excepción. De esta manera, se consigue que se deje de ejecutar la función oops nada más hacer el raise, y la función que llamó a ésta reciba la excepción.
def caller():
try:
oops(KeyError)
except IndexError:
print("Received an IndexError exception")
except KeyError:
print("Received an KeyError exception")
Define la función caller, que no recibe argumentos. En esta función hay un bloque try/except. En este tipo de bloques se ejecuta el cuerpo del try, y si durante la ejecución se recibe alguna excepción, se comprueba con los bloques except que haya (al menos uno).
En este caso el primer bloque except comprueba si se recibió la excepción IndexError, en cuyo caso ejecuta la sentencia print() que se ve ahí. El print() va con paréntesis porque es la sintaxis exigida por Python 3000 (recién salido del horno), y soportada por versiones anteriores. Hasta ahora había sido habitual verlo como está pero sin esos paréntesis.
Esta función sencillamente llama a la función oops(), y le pasa como parámetro una excepción KeyError. Esa función lanzará entonces una excepción KeyError, que será recibida por el except KeyError de caller() (dado que la llamada a oops() se hizo dentro del try y que existe un except que captura esa excepción). Finalmente imprimirá el mensaje correspondiente según la excepción capturada.
if __name__=="__main__":
caller()
__name__ es una variable built-in que indica el nombre del módulo que se está ejecutando. Si se trata del programa principal, tiene el valor ‘__main__’. Este código es habitual verlo en módulos, donde suele tener la función de ejecutar una serie de test sobre el propio módulo cuando es ejecutado en vez de cuando es incluido en otro script.
Por otra parte, ya contestando a la última pregunta, KeyError e IndexError son dos identificadores de sendas clases derivadas de la clase Exception. Tanto Exception, como sus derivadas KeyError e IndexError están alojadas en el built-in, es decir, que vienen de por sí en el lenguaje, igual que viene el tipo lista o la función print().
La regla LEGB hace referencia a Local, Enclosing function locals, Global y Built-in. Es decir, que python busca los identificadores de las variables en esos ámbitos, por ese orden: primero en variables locales (los argumentos también son variables locales) dentro de la función, luego en variables locales de funciones dentro de las que está definida la función actual, luego a nivel global, y por último, si no lo encontró en ningún nivel anterior, busca en el built-in.
Así, sí hay una variable global foo y otra local del mismo nombre e una función, al hacerle referencia desde la función, se hará referencia a su variable local, y no a la global.
Nota: la identación de código en WordPress la he hecho con el plugin Code-snippet. Y no es recomendable usar el modo visual con él, ya que se cepilla los espacios/tabulaciones.
September 19, 2008 at 15:05 · Filed under Python, Sistemas de ficheros
Ya me picaba el gusanillo tras tanto examen y tanta gaita, así que esta vez ha tocado hacer una función que recorra los ficheros del directorio actual y de todos sus subdirectorios. Se basa en una función que mediante las funciones
os.stat()
para saber el tipo de fichero,
os.listdir()
para sacar el contenido de un directorio y una cola para guardar los ficheros que quedan por analizar.
Importamos las funciones necesarias de sus respectivos módulos:
from os import stat,listdir
from os.path import join
from stat import S_ISDIR,S_ISCHR,S_ISBLK,S_ISREG,S_ISFIFO,S_ISLNK,S_ISSOCK
Todas las
stat.S_ISxx()
se pueden sacar también de
os.stat.isXX()
, según la documentación del módulo stat –donde se puede encontrar una función similar a la implementada, pero con recursividad en vez de usar una cola.
Ahora la función (siento que la identación esté hecha con ___, pero es que el editor de WordPress no está hecho para meter código):
def walktree(origen):
____ficheros=[(None,origen),]
cargar la ruta inicial. El primer elemento de la tupla no sería necesario, no habría por qué usar una tupla. La estoy usando para un uso posterior que le quiero dar a esta función de recorrido de directorios.
____while ficheros:
mientras queden ficheros
____ ____ (parent,file)=ficheros.pop()
sacamos el siguiente fichero de la lista (ignorad
parent
)
____ ____print “Fichero: %s”%(file)
____ ____tipo=stat(file).st_mode
obtenemos mediante
os.stat()
el modo del fichero (lo que incluye los permisos, pero también el tipo). Según de qué tipo sea imprimiremos una cosa u otra en pantalla:
____ ____ if S_ISDIR(tipo):
Es un directorio: además de decir que es un directorio…
____ ____ ____ print “Directory”
____ ____ ____ for nuevo_fichero in listdir(file):
…debemos leer sus contenidos con
os.listdir()
e incluirlos en la lista ficheros. Nótese que
listdir()
no devuelve ni el directorio
‘.’
, ni
‘..’
, lo que nos evita ciclos que harían que el algoritmo no acabara nunca hasta agotar la memoria
____ ____ ____ ____ ficheros.insert(0,
____ ____ ____ ____ ____ (None,join(file,nuevo_fichero)))
____ ____ elif S_ISCHR(tipo):
y vamos comprobando otros tipos
____ ____ ____ print “Char. device”
____ ____ elif S_ISBLK(tipo):
____ ____ ____ print “Block device”
____ ____ elif S_ISREG(tipo):
____ ____ ____ print “Regular file”
____ ____ elif S_ISFIFO(tipo):
____ ____ ____ print “Named pipe”
____ ____ elif S_ISLNK(tipo):
____ ____ ____ print “Soft link”
____ ____ elif S_ISSOCK(tipo):
____ ____ ____ print “Socket”
if __name__==’__main__’:
____ walktree(‘.’)
Next entries »