Programa Como Un Pythonista
Programa Como Un Pythonista
Bitácora
Traducciones
Tutorial de Python
un habitante de Montreal,
padre de dos grandes niños, marido de una mujer muy especial,
programador en Python a tiempo completo,
autor del proyecto Docutils y de reStructuredText,
un editor de las Propuestas de Mejora de Python (Python Enhancement
Proposals o PEPs),
uno de los organizadores de la PyCon 2007, y coordinador de PyCon 2008,
un miembro de la Fundación de Software Python,
Director de la Fundación el año pasado, y su Secretario actual.
1 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
...
—Tim Peters
Este poema en particular comenzó como una especie de broma, pero contiene
grandes verdades acerca de la filosofía detrás de Python. El Zen de Python ha
sido formalizado en el PEP 20, en cuyo resumen podemos leer:
2 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
—[Link]
En caso de duda:
import this
Estilo de Programación: La
legibilidad cuenta
Los programas deben escribirse para que los lean las personas, y sólo
de forma circunstancial para que los ejecuten las máquinas.
3 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
[Link]
La comunidad Python tiene sus propios estándares en lo que al estilo del código
se refiere, listados en el PEP 8. Estos estándares son distintos a los de otras
comunidades, como C, C++, C#, Java, VisualBasic, etc.
Espacios en blanco 1
4 espacios por cada nivel de indentación.
Espacios en blanco 2
Añade un espacio después de las comas "," en diccionarios, listas, tuplas, y
listas de parámetros, y después de los dos puntos ":" en los diccionarios,
pero no antes.
Pon espacios alrededor de las asignaciones y las comparaciones (excepto
en las listas de parámetros).
No coloques espacios justo después de abrir un paréntesis o antes de
cerrar un paréntesis o justo antes de una lista de parámetros.
No dejes espacios al inicio o final de las cadenas de documentación.
4 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
l = [clave, valor]
return d, l
Nombres
minusculas_con_guiones para funciones, métodos, atributos
Líneas largas
Mantén las líneas de código por debajo de los 80 caracteres.
Las barras invertidas no son un mecanismo robusto; tienen que situarse al final
de la línea. Si añadieras un espacio después de la barra invertida, dejaría de
funcionar. Además, afean el código.
Cadenas largas
El parseador concatena los literales de cadena adyacentes automáticamente:
>>> print 'u' 'n' "o"
5 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
uno
Los espacios entre las cadenas no son necesarios, pero ayudan a la legibilidad.
Se puede utilizar cualquier tipo de comillas:
>>> print 't' r'\/\/' """o"""
t\/\/o
La cadena con una "r" como prefijo es una cadena "raw" (cruda). En las cadenas
raw las barras invertidas no se consideran caracteres de escape. Son útiles para
las expresiones regulares y las rutas del sistema de ficheros de Windows.
"""Triples
comillas
dobles"""
'''\
Triples
comillas
simples\
'''
Observa que en el último ejemplo (el de las triples comillas simples), se utilizan
barras invertidas para escapar los caracteres de nueva línea. Esto elimina los
retornos de carro extra, proporcionando una estructura muy legible, con el texto
y las comillas justificadas a la izquierda. Las barras invertidas se deben colocar
al final de las respectivas líneas.
6 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Sentencias compuestas
Bien:
if foo == 'bla':
haz_algo()
haz_primero()
haz_segundo()
haz_tercero()
Mal:
Cadenas de documentación y
comentarios
Cadenas de documentación = Cómo usar el código
Los comentarios explican el por qué, y están dirigidos a las personas que van
a mantener tu código. Puedes incluir notas para tí mismo, como:
7 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Ambos grupos te incluyen a ti, ¡así que asegúrate de escribir buenas cadenas de
documentación y comentarios!
[Link]
8 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Popurrí de modismos
Una selección de idioms o modismos pequeños y útiles.
Vamos a comenzar con algunos más sencillos e iremos aumentando el nivel poco
a poco.
Intercambiar valores
En otros lenguajes:
temporal = a
a = b
b = temporal
En Python:
b, a = a, b
Puede que hayas visto esto antes. ¿Pero sabías cómo funciona?
Son útiles para usar en bucles que iteran sobre datos estructurados:
l(L) es la lista que acabamos de crear (los datos de David). Así que personas es
una lista que contiene dos elementos, siendo cada uno de ellos una lista de 3
9 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
elementos.
>>> personas = [l, ['Guido', 'BDFL', 'desconocido']]
>>> for (nombre, rango, tlf) in personas:
... print nombre, tlf
...
David +1-514-555-1234
Guido desconocido
>>> tuple()
()
10 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Un fallo muy común es añadir una coma incluso cuando lo que buscas no es una
tupla. Es fácil no darse cuenta de que hay una coma en el código:
>>> valor = 1,
>>> valor
(1,)
Por lo tanto, si ves una tupla en un lugar donde no debería existir, ¡comprueba
las comas!
"_" interactivo
Esta es una característica muy útil aunque, sorprendentemente, muy poca gente
la conoce.
En el intérprete interactivo, cada vez que evalúas una expresión o llamas a una
función, el resultado se almacena en una variable temporal llamada _ (un guión
bajo):
>>> 1 + 1
2
>>> _
2
Ahora queremos unir todas las cadenas en una cadena más larga. Especialmente
cuando tenemos un gran número de subcadenas...
No hagas esto:
resultado = ''
for s in colores:
resultado += s
Si estás trabajando con unas pocas docenas o cientos de cadenas, puede que no
haya mucha diferencia. Pero deberías acostumbrarte a construir las cadenas de
forma más efectiva, porque con miles de cadenas o con bucles, hay una gran
diferencia.
O comas y espacios:
12 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Para que la frase tenga más sentido gramáticamente, pondremos comas entre
todos los valores a excepción del último par, donde queremos la palabra "o". El
particionado de listas es la herramienta adecuada para el trabajo. Con [:-1]
obtenemos todos los valores a excepción del último, y los unimos utilizando una
coma y un espacio.
Salida:
Elige rojo, azul, verde o amarillo
Añadimos las partes a una lista de forma que podamos usar el método join por
cuestiones de eficiencia.
13 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Mal:
[Link]()crea una lista estática de las claves del diccionario. De otra forma, se
lanzaría una excepción "RuntimeError: dictionary changed size during iteration"
(el tamaño del diccionario cambió durante la iteración).
# haz esto:
if clave in d:
...hacer algo con d[clave]
# no esto:
if d.has_key(clave):
...hacer algo con d[clave]
14 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
acciones = {}
for (portfolio, accion) in datos:
if portfolio in acciones:
acciones[portfolio].append(accion)
else:
acciones[portfolio] = [accion]
Si el valor por defecto es costoso de calcular, puede que prefieras usar la clase
defaultdict, que veremos en breve.
15 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
El método setdefault del diccionario devuelve el valor por defecto, pero nosotros
lo ignoramos. Lo que estamos haciendo es aprovechar un efecto secundario de
setdefault, que es el de asignar el valor del diccionario sólo si no existe ya un
valor para esa clave.
defaultdict
Nuevo en Python 2.5.
defaultdictes una novedad de Python 2.5, parte del módulo collections. defaultdict
es idéntico a los diccionarios normales, a excepción de un par de cosas:
import collections
d = [Link](...)
acciones = defaultdict(list)
for (portfolio, accion) in datos:
acciones[portfolio].append(accion)
16 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Para obtener un diccionario con valores por defecto de 0 podríamos usar int
como función de factoría por defecto:
navs = defaultdict(int)
for (portfolio, accion, posicion) in datos:
navs[portfolio] += posicion * precios[accion]
De todas formas deberías tener cuidado con defaultdict. Puedes encontrarte con
excepciones de tipo KeyError al intentar acceder a claves de instancias de
defaultdict. Es necesario usar un condicional "clave in dict" si necesitas
comprobar la existencia de una clave en concreto.
Construyendo y dividiendo
diccionarios
Aquí tenéis una técnica útil para construir un diccionario a partir de dos listas (o
secuencias): una lista de claves, y otra lista de valores.
nombre = ['John', 'Eric', 'Terry', 'Michael']
apellido = ['Cleese', 'Idle', 'Gilliam', 'Palin']
>>> [Link](pythons)
{'John': 'Cleese',
'Michael': 'Palin',
'Eric': 'Idle',
'Terry': 'Gilliam'}
Observa que el orden del resultado de .keys() y .values() es distinto del de los
elementos de las listas utilizadas para construir el diccionario. El orden de
entrada es diferente del orden de salida. Esto es debido a que los diccionarios
son colecciones no ordenadas. Por otro lado, el orden será siempre consistente
(esto es, el orden de las claves se corresponderá con el de los valores), siempre
y cuando el diccionario no se haya cambiado entre llamadas.
17 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
# y menos esto:
if items != []:
pass
Valores booleanos
Los nombres True y False son instancias del tipo bool, valores Booleanos. Como
None, sólo hay una instancia de cada uno de ellos.
False True
False (== 0) True (== 1)
"" (cadena vacía) cualquier cadena menos "" (" ",
"cualquiera")
18 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
usuario, utiliza los métodos especiales __nonzero__ o __len__. Usa __len__ si tu clase
es un contenedor para el que tiene sentido el tamaño:
class MiContenedor(object):
def __len__(self):
"""Devuelve mi tamanyo."""
return len([Link])
class MiClase(object):
def __nonzero__(self):
"""Devuelve mi valor booleano (True o False)."""
# Esto puede ser tan complejo como queramos:
return bool([Link])
- o -
i = 0
for item in items: for i in range(len(items)):
print i, item print i, items[i]
i += 1
19 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
La función enumerate toma como parámetro una lista y devuelve pares (indice,
elemento):
>>> print list(enumerate(items))
[(0, 'cero'), (1, 'uno'), (2, 'dos'), (3, 'tres')]
# compara: # compara:
indice = 0 for i in range(len(items)):
for item in items: print i, items[i]
print indice, item
indice += 1
20 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Al asignar otro valor a la misma variable lo que ocurre es que el valor que
contiene la caja se sustituye por el nuevo:
a = 2;
Al asignar una variable a otra se realiza una copia del valor y se coloca en la
nueva caja:
int b = a;
"b" es una segunda caja, con una copia del entero 2. La caja "a" tiene una copia
totalmente independiente.
21 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
a = 1
En este caso, el objeto entero 1 tiene una etiqueta con texto "a".
a = 2
El objeto entero original 1 ya no tiene la etiqueta "a". Puede que siga existiendo,
pero ya no podemos accederlo a través del nombre "a". (Cuando un objeto no
tiene más referencias o etiquetas asociadas, se elimina de memoria.)
b = a
El nombre "b" es sólo una segunda etiqueta añadida al mismo objeto que "a".
Aunque no aprendas nada más de este tutorial, espero que al menos hayas
entendido cómo funcionan los nombres en Python. Entender este punto te dará
buenos dividendos, ayudándote a evitar casos como este:
22 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
parámetros
Este es un fallo muy común entre los novatos. Incluso programadores más
experimentados pueden cometer el mismo fallo si no entienden los nombres en
Python.
def anyadir_erroneo(nuevo_item, una_lista=[]):
una_lista.append(nuevo_item)
return una_lista
El problema con esto es que el valor por defecto de una_lista, una lista vacía, se
evalúa en el momento de definir la función. De modo que cada vez que llamas a
la función, obtienes el mismo valor por defecto. Prueba la función varias veces:
>>> print anyadir_erroneo('uno')
['uno']
% Formateo de cadenas
El operador % de Python funciona de forma similar al de la función sprintf de C.
Los valores interpolados deben ajustarse a la plantilla; en este caso tenemos dos
valores, una tupla.
nombre = 'David'
mensajes = 3
texto = ('Hola %s, tienes %i mensajes'
23 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
% (nombre, mensajes))
print texto
Salida:
En este caso especificas los nombres de los valores a interpolar, los cuales se
buscan en el diccionario que se pasa como argumento.
Este es un mecanismo muy potente. Con esto, puedes hacer todo el formateo de
cadenas que quieras sin tener que preocuparte de ajustar los valores a
interpolar con la plantilla.
Pero el poder puede ser peligroso. ("Un gran poder conlleva una gran
responsabilidad.") Si utilizas locals() con una plantilla proporcionada de forma
externa, expones tu espacio de nombres local completamente al llamador. Esto
es algo a tener en cuenta.
24 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
pprint es un módulo muy útil. Si no lo conoces todavía, intenta jugar un poco con
él. ¡Facilita muchísimo la depuración de las estructuras de datos!
Comprensión de listas
La comprensión de listas ("listcomps" para abreviar) son atajos sintácticos para
el siguiente patrón:
nueva_lista = []
for item in una_lista:
if condicion(item):
nueva_lista.append(fn(item))
La compresión de listas es muy clara y concisa, hasta cierto punto. Puedes tener
varios bucles for y varias condiciones if, pero a partir de dos o tres, o si las
condiciones son algo complejas, te sugiero utilizar bucles for normales.
25 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Como un bucle:
total = 0
for num in range(1, 101):
total += num * num
26 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Regla general:
Ejemplo reciente:
codigos_meses = dict((fn(i+1), codigo)
for i, codigo in enumerate('FGHJKMNQUVXZ')
for fn in (int, str))
27 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Ordenando
Ordenar listas en Python es muy sencillo:
a_list.sort()
(Ten en cuenta que se ordena la lista original, el método sort no devuelve una
lista o una copia de la lista ordenada.)
Pero, ¿qué ocurre si tenemos una lista de datos a ordenar, pero el orden no es el
normal (es decir, ordenar por la primera columna, después la segunda, etc.)?
Puede que necesitemos ordenar por la segunda columna y luego por la cuarta.
Podemos usar el método sort indicando una función propia para comparar:
def mi_comparador(item1, item2):
returm cmp((item1[1], item1[3]),
(item2[1], item2[3]))
una_lista.sort(mi_comparador)
En lugar de crear nuestras propias funciones para comparar, creamos una lista
auxiliar que se ordene de forma natural:
# Decorar:
a_ordenar = [(item[1], item[3], item)
for item in una_lista]
# Ordenar:
a_ordenar.sort()
# Desdecorar:
una_lista = [item[-1] for item in a_ordenar]
28 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
La primera línea crea una lista que contiene tuplas: copias de elementos a
ordenar en orden de prioridad, seguidos de los datos completos.
a_ordenar.sort(key=mi_clave)
La función mi_clave se llama una vez para cada elemento de la lista a_ordenar.
Puedes crear tus propias funciones para calcular la clave, o utilizar cualquier
función de un sólo parámetro que se ajuste a tus necesidades:
Generadores
Ya hemos visto las expresiones generadoras. Podemos crear expresiones
generadoras tan complejas como queramos, como funciones:
def mi_generador_range(parada):
valor = 0
while valor < parada:
29 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
yield valor
valor += 1
for i in mi_generador_range(10):
haz_algo(i)
Entonces Python llama repetidamente al método next del iterador, asigna el valor
devuelto al contador del bucle (i en este caso), y ejecuta el código indentado. El
proceso se repite una y otra vez, hasta que se lanza la excepción StopIteration, o
se ejecuta una sentencia break.
Un bucle for puede tener una clausula else, cuyo código se ejecuta después de
que el iterador se quede sin elementos, pero no si se sale al ejecutar un break.
Esta distinción permite usos muy elegantes. Las cláusulas else no se suelen
utilizar mucho con los bucles for, pero pueden ser muy útiles. Algunas veces una
cláusula else es la mejor forma de expresar la lógica que necesitas.
Por ejemplo, si necesitas comprobar que una condición se cumple para todos los
elementos de una secuencia:
for item in secuencia:
if condition(item):
break
else:
raise Exception('La condicion no se satisface.')
Generadores de ejemplo
30 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Esto es posible porque los archivos soportan el método next, como otros
iteradores: listas, tuplas, diccionarios (para sus claves), generadores.
Aunque hay que tener cuidado con una cosa: debido a la forma en la que
funciona el buffer de datos, no puedes mezclar los métodos .next y .read* a menos
que utilices Python 2.5+.
Excepciones
Si x debe ser una cadena para que el código funcione, por qué no escribir
str(x)
31 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Nota: Especifica siempre las excepciones a capturar. Nunca uses cláusulas except
sin indicar el tipo de la excepción. Esto podría capturar excepciones no
esperadas, haciendo que tu código fuera más dificil de depurar.
Importando
from modulo import *
Es probable que hayas visto esta forma con un "carácter comodín" de las
sentencias import. Puede que incluso te guste. No lo utilices.
LUKE: ¿Es mejor from module import * que los imports explícitos?
LUKE: Pero ¿cómo sabré por qué los imports explícitos son mejores
que la forma con el carácter comodín?
¡Nunca!
32 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
El uso de from module import * conlleva polución del espacio de nombres. Tendrás
montones de cosas en el espacio de nombres local que no esperabas tener.
Puede que te encuentres con nombres importados sobreescribiendo nombres
definidos en local. No podrás distinguir fácilmente de dónde vienen algunos
nombres. Aunque pueda ser un atajo conveniente, no debería utilizarse en
código en producción.
Es mucho mejor:
En su lugar,
O importa un módulo con un nombre largo usando un nombre más corto (alias):
import modulo_con_nombre_largo as mod
[Link]
33 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Modulos y Scripts
Para crear un módulo que se pueda importar y también ejecutar como si de un
script se tratara:
if __name__ == '__main__':
# codigo del script
Estructura de un módulo
"""Cadena de documentacion del modulo"""
# imports
# constantes
# clases de tipo excepcion
# funciones para uso publico
# clases
# funciones y clases internas
def main(...):
...
if __name__ == '__main__':
estado = main()
[Link](estado)
34 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
"""
Cadena de documentacion del modulo.
"""
import sys
import optparse
def procesar_linea_comandos(argv):
"""
Devuelve una tupla de dos elementos: (objeto preferencias, lista args).
`argv` es una lista de argumentos, o `None` para ``[Link][1:]``.
"""
if argv is None:
argv = [Link][1:]
# inicializamos el parser:
parser = [Link](
formatter=[Link](width=78),
add_help_option=None)
# cualquier otra cosa que haya que hacer con las preferencias y los argumentos
def main(argv=None):
prefs, args = procesar_linea_comandos(argv)
# aqui iria el codigo de la aplicacion, como:
# ejecutar(prefs, args)
return 0 # exito
if __name__ == '__main__':
estado = main()
[Link](estado)
Paquetes
paquete/
__init__.py
[Link]
subpaquete/
35 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
__init__.py
[Link]
Ejemplo:
import paquete.modulo1
from [Link] import modulo2
from [Link].modulo2 import nombre
No reinventes la rueda
Antes de escribir cualquier código,
➔➔➔➔
[Link]
36 of 37 08/18/2017 01:21 PM
Programa como un Pythonista: Python Idiomático [Link]
Referencias
"Python Objects", Fredrik Lundh, [Link]
[Link]
"How to think like a Pythonista", Mark Hammond, [Link]
/crew/mwh/hacks/[Link]
"Python main() functions", Guido van Rossum, [Link]
/weblogs/[Link]?thread=4829
"Python Idioms and Efficiency", [Link]
/[Link]
"Python track: python idioms", [Link]
/cs11/material/python/misc/python_idioms.html
"Be Pythonic", Shalabh Chaturvedi, [Link]
/Be_Pythonic2
"Python Is Not Java", Phillip J. Eby, [Link]
[Link]
"What is Pythonic?", Martijn Faassen, [Link]
/weblog/2005/08/06/0
"Sorting Mini-HOWTO", Andrew Dalke, [Link]
/Sorting
"Python Idioms", [Link]
"Python FAQs", [Link]
37 of 37 08/18/2017 01:21 PM