Conclusiones:
- Sigue siendo mi lenguaje preferido.
- El manejo de husos horarios en Python es la peor experiencia de programación de mi vida.
- Los tutoriales de iniciación obvian unas pocas cosas muy importantes al empezar a programar en serio.
if var
Es el "idiom" más recurrente de Python, y normalmente se explica como "si var tiene algo".Si se aplica siempre está lógica, morirás en el intento, porque realmente significa:
"si var no es None y no es el valor por defecto para este tipo de variable".Por tanto estas cosas son True:
- bool('0')
- bool(' ')
- bool(-1)
- bool([0])
- bool(0)
- bool('')
- bool(None)
- bool([])
Copia de colecciones
Cuando copiamos una lista o diccionario, estamos copiando en realidad la referencia al objeto, con lo cual si modificamos, el original, se modifica también la copia.Para evitar esto hay que usar el método 'deepcopy' de la librería 'copy':
lista2 = copy.deepcopy(lista1)En los casos en que nos hay colecciones anidadas, puede bastar una copia superficial (shallow), que puede hacer de estas formas con los diccionarios:
dicc2 = dict(dicc1) dicc2 = dicc1.copy() dicc2 = copy.copy(dicc1)y de estas formas con las listas:
lista2 = list(lista1) lista2 = copy.copy(lista1)
Paso de variables a funciones
En realidad sigo sin tener claro como funciona, pero sí puedo advertir de serios efectos colaterales si no se tienen en cuenta ciertas cosas.En Python se utiliza el paso por valor de referencias a objetos , aunque los valores inmutables (enteros, tuplas...) se comportan como paso por valor. Esto provoca que al pasar un objeto mutable no siempre estemos modificando el objeto original. P.e. Estas dos expresiones son idénticas donde se define el diccionario, pero son completamente distintas si se utilizan sobre una referencia recibida:
En cuanto a las variables globales, se puede acceder directamente a ellas, para lectura, desde dentro de una función, siempre que no exista una variable local con su mismo nombre. Pero si intentamos modificar su valor, en realidad creará al vuelo una variable local con su mismo nombre. Para poder modificar una variable global debemos declarla así, dentro de la función:dicc = {} # ésta no borra el diccionario "original" dicc.clear() # ésta sí
Nota: si la variable global es mutable (p.e. un diccionario) y no la declaramos con 'global' dentro de una función, no podremos hacer que apunte a otro objeto, pero sí podremos alteraTruer su contenido. Por tanto, nuevamente, dicc={} no borraría el diccionario original, pero dicc.clear(), sí, y dicc['campo1']=3 alteraría el contenido de la variable global.global mi_variable_global
Vaciar lista
Esto crea una lista vacía, pero no vacía una existente:l = []Para vaciar una existente no existe un método como el 'clear' de los diccionarios, sino que hay que hacer:
l[:] = [] # equivalente: del l[:]No es muy fácil de recordar, pero como se intuye, mediante slicing podemos vaciar solo una parte de la lista:
l[2:3] = [] # equivalente: del l[2:3]
Operadores * y **
Ambos tienen sentido solo al pasar parámetros a una función. Hablaré de sucesión o secuencia de parámetros, ya que la palabra "lista" en Python es un tipo de datos. Para mí, la secuencia de parámetros es una cosa rara, que bien se podría asimilar a unos de los tipos o darle una entidad más explícita.La secuencia de parámetros en la llamada a una función solo puede contener valores sin nombre y valores con nombre separados por comas. Estos últimos tienen que ir al final obligatoriamente, aunque el orden entre ellos no tiene por qué ser el mismo que en la definición de la función.
Al hilo de esto:
- * convierte una lista en una sucesión de parámetros sin nombre
- ** convierte un diccionario en una sucesión de parámetros con nombre
Ordenar un diccionario
En Python 2.7 se introdujeron los SortedDict. Para Python 2.6 se puede usar una implementación completa, añadiendo una librería, o bien, en algunos casos, basta con usar este truco simple para recorrerlo ordenado (siendo d un diccionario):for k in sorted(d):
print d[k]
También:
- sorted(d.iterkeys())
- sorted(d.keys())
Doctest
Considero Doctest muy útil por ser una forma tremendamente compacta de implementar pruebas unitarias, pero tiene algún comportamiento extraño derivado del hecho de que la parte de comprobación, es una cadena de texto:- El valor que devuelve no siempre tiene la forma esperada, en el caso de un string, por lo que la cadena de comprobación puede tener que ser diferente a la que se visualiza.
- No se puede poner ningún tipo de expresión en la cadena de comprobación, por lo que en estos casos, tenemos que evaluar una comparación contra dicha expresión y comprobar si devuelve True o False.