Apéndice C. Trucos y consejos
Capítulo 1. Instalación de Python
Capítulo 2. Su primer programa en Python
- 2.1. Inmersión
 |
| En el IDE ActivePython para Windows puede ejecutar el programa
de Python que esté editando escogiendo
-> (Ctrl-R). La salida se muestra en la pantalla interactiva.
|
 |
| En el IDE de Python de Mac OS puede ejecutar un programa de
Python con
-> (Cmd-R), pero hay una opción importante que debe activar antes.
Abra el fichero .py en el IDE, y muestre el menú de
opciones pulsando en el triángulo negro en la esquina superior derecha de
la ventana, asegurándose de que está marcada la opción
. Esta
preferencia está asociada a cada fichero por separado, pero sólo tendrá que
marcarla una vez por cada uno.
|
 |
| En sistemas compatibles con UNIX (incluido Mac OS X), puede
ejecutar un programa de Python desde la línea de órdenes:
python odbchelper.py |
- 2.2. Declaración de funciones
 |
| En Visual Basic las funciones (devuelven un valor) comienzan con
function, y las subrutinas (no devuelven un valor)
lo hacen con sub. En Python no tenemos subrutinas.
Todo son funciones, todas las funciones devuelven un valor (incluso si es
None) y todas las funciones comienzan por def.
|
 |
| En Java, C++ y otros lenguajes de tipo estático debe
especificar el tipo de dato del valor de retorno de la función y de cada
uno de sus argumentos. En Python nunca especificará de forma explícita
el tipo de dato de nada. Python lleva un registro interno del tipo de
dato basándose en el valor asignado.
|
- 2.3. Documentación de funciones
 |
| Las comillas triples también son una manera sencilla de definir una
cadena que contenga comillas tanto simples como dobles, como
qq/.../ en Perl.
|
 |
| Muchos IDE de Python utilizan la cadena de documentación para proporcionar
una ayuda sensible al contexto, de manera que cuando escriba el nombre
de una función aparezca su cadena de documentación como ayuda. Esto puede ser
increíblemente útil, pero lo será tanto como buenas las cadenas de documentación que
usted escriba.
|
- 2.4. Todo es un objeto
 |
| import en Python es como require en Perl. Una vez que hace
import sobre un módulo de Python, puede acceder a sus funciones con
módulo.función;
una vez que hace require sobre un módulo de Perl, puede acceder a sus
funciones con
módulo::función.
|
- 2.5. Sangrado (indentado) de código
 |
| Python utiliza retornos de carro para separar sentencias y los
dos puntos y el sangrado para reconocer bloques de código. C++ y Java
usan puntos y coma para separar sentencias, y llaves para indicar bloques
de código.
|
- 2.6. Prueba de módulos
 |
| Al igual que C, Python utiliza == para la
comparación y = para la asignación. Al contrario que C,
Python no permite la asignación embebida, de manera que no existe la
posibilidad de asignar un valor accidentalmente donde deseaba hacer una
comparación.
|
 |
| En MacPython, hay que dar un paso adicional para hacer que funcione
el truco if __name__. Muestre el menú de opciones pulsando el triángulo negro
en la esquina superior derecha de la ventana, y asegúrese de que está
marcado .
|
Capítulo 3. Tipos de dato nativos
- 3.1. Presentación de los diccionarios
 |
| Un diccionario en Python es como un hash en Perl. En Perl, las
variables que almacenan hashes siempre empiezan con un carácter
%. En Python las variables se pueden llamar de
cualquier manera, y Python sabe su tipo internamente.
|
 |
| Un diccionario en Python es como una instancia de la clase
Hashtable de Java.
|
 |
| Un diccionario en Python es como una instancia del objeto
Scripting.Dictionary de Visual Basic.
|
- 3.1.2. Modificar diccionarios
 |
| Los diccionarios no tienen concepto de orden entre sus elementos. Es
incorrecto decir que los elementos están “desordenados”, ya
que simplemente no tienen orden. Esto es una distinción importante que le
irritará cuando intente acceder a los elementos de un diccionario en un
orden específico y repetible (por ejemplo, en orden alfabético por clave).
Hay maneras de hacer esto, pero no vienen de serie en el
diccionario.
|
- 3.2. Presentación de las listas
 |
| Una lista de Python es como un array en Perl. En Perl, las
variables que almacenan arrays siempre empiezan con el carácter
@; en Python, las variables se pueden llamar de
cualquier manera, y Python se ocupa de saber el tipo que tienen.
|
 |
| Una lista en Python es mucho más que un array en Java (aunque
puede usarse como uno si es realmente eso todo lo que quiere en esta
vida). Una mejor analogía podría ser la clase
ArrayList, que puede contener objetos arbitrarios y
expandirse de forma dinámica según se añaden otros nuevos.
|
- 3.2.3. Buscar en listas
 |
Antes de la versión 2.2.1, Python no tenía un tipo booleano. Para
compensarlo, Python aceptaba casi cualquier cosa en un contexto booleano
(como una sentencia if), de acuerdo a las siguientes reglas:
- 0 es falso; el resto de los números son
verdaderos.
- Una cadena vacía ("") es falso,
cualquier otra cadena es verdadera.
- Una lista vacía ([]) es falso; el resto
de las listas son verdaderas.
- Una tupla vacía (()) es falso; el resto
de las tuplas son verdaderas.
- Un diccionario vacío ({}) es falso;
todos los otros diccionarios son verdaderos.
Estas reglas siguen aplicándose en Python 2.2.1 y siguientes, pero
ahora además puedes usar un verdadero booleano, que tiene el valor de
True o False. Tenga en cuenta las
mayúsculas; estos valores, como todo lo demás en Python, las
distinguen.
|
- 3.3. Presentación de las tuplas
 |
| Las tuplas se pueden convertir en listas, y viceversa. La función
incorporada tuple toma una lista y devuelve una tupla con los mismos
elementos, y la función list toma una tupla y devuelve una lista. En
efecto, tuple "congela" una lista, y list "descongela" una
tupla.
|
- 3.4. Declaración de variables
 |
| Cuando una orden se divide en varias líneas con la marca de
continuación de línea (“\”), las
siguientes líneas se pueden sangrar de cualquier manera; el habitual
sangrado astringente de Python no se aplica aquí. Si su IDE de
Python autosangra la línea a continuación, probablemente debería
aceptar este comportamiento a menos que tenga una imperiosa razón para no
hacerlo.
|
- 3.5. Formato de cadenas
 |
| El formato de cadenas en Python usa la misma sintaxis que la
función sprintf en C.
|
- 3.7. Unir listas y dividir cadenas
 |
| join funciona sólo sobre listas de cadenas; no hace ningún tipo de
conversión de tipos. Juntar una lista que tenga uno o más elementos que no
sean cadenas provocará una excepción.
|
 |
| unacadena.split(delimitador,
1) es una forma útil de buscar una subcadena dentro de una
cadena, y trabajar después con todo lo que hay antes de esa subcadena (que
es el primer elemento de la lista devuelta) y todo lo que hay detrás (el
segundo elemento).
|
Capítulo 4. El poder de la introspección
- 4.2. Argumentos opcionales y con nombre
 |
| Lo único que necesita para invocar a una función es especificar un
valor (del modo que sea) para cada argumento obligatorio; el modo y el
orden en que se haga esto depende de usted.
|
- 4.3.3. Funciones incorporadas
 |
| Python se acompaña de excelentes manuales de referencia, que
debería usted leer detenidamente para aprender todos los módulos que
Python ofrece. Pero mientras en la mayoría de lenguajes debe usted
volver continuamente sobre los manuales o las páginas de manual para
recordar cómo se usan estos módulos, Python está autodocumentado
en su mayoría.
|
- 4.7. Utilización de las funciones lambda
 |
| Las funciones lambda son una cuestión de estilo. Su
uso nunca es necesario. Puede definir una función normal separada y usarla
en cualquier sitio en que pueda utilizarse una lambda.
Yo las utilizo en lugares donde deseo encapsulación, código no
reutilizable que no ensucie mi propio código con un montón de pequeñas
funciones de una sola línea.
|
- 4.8. Todo junto
 |
| En SQL, se utiliza IS NULL en vez de
= NULL para comparar un valor nulo. En Python puede
usar tanto == None como is None,
pero is None es más rápido.
|
Capítulo 5. Objetos y orientación a objetos
- 5.2. Importar módulos usando from módulo import
 |
| from módulo import * en Python es como use
módulo en Perl; import módulo en
Python es como require
módulo en Perl.
|
 |
| from módulo import * en Python es como import
módulo.* en Java; import módulo en
Python es como import
módulo en Java.
|
 |
| Utilice from module import * lo menos posible,
porque hace difícil determinar de dónde vino una función o atributo en
particular, y complica más la depuración y el refactorizado.
|
- 5.3. Definición de clases
 |
| La sentencia pass de Python es como unas llaves vacías
({}) en Java o C.
|
 |
| En Python, el ancestro de una clase se lista entre paréntesis
inmediatamente tras el nombre de la clase. No hay palabras reservadas
especiales como extends en Java.
|
- 5.3.1. Inicialización y programación de clases
 |
| Por convención, el primer argumento de cualquier clase de Python
(la referencia a la instancia) se denomina self. Este argumento cumple
el papel de la palabra reservada this en C++ o Java, pero self no
es una palabra reservada en Python, sino una mera convención. De todas
maneras, por favor no use otro nombre sino self; es una convención muy
extendida.
|
- 5.3.2. Saber cuándo usar self e __init__
 |
| Los métodos __init__ son opcionales, pero cuando define uno, debe
recordar llamar explícitamente al método __init__ del ancestro (si define
uno). Suele ocurrir que siempre que un descendiente quiera extender el
comportamiento de un ancestro, el método descendiente deba llamar al del
ancestro en el momento adecuado, con los argumentos adecuados.
|
- 5.4. Instanciación de clases
 |
| En Python, simplemente invocamos a una clase como si fuese una
función para crear una nueva instancia de la clase. No hay operador new
explícito como en C++ o Java.
|
- 5.5. Exploración de UserDict: Una clase cápsula
 |
| En el IDE ActivePython en Windows, puede abrir rápidamente
cualquier módulo de su ruta de bibliotecas mediante
-> (Ctrl-L).
|
 |
| Java y Powerbuilder admiten la sobrecarga de funciones por lista
de argumentos, es decir una clase puede tener varios métodos con el mismo
nombre, pero con argumentos en distinta cantidad, o de distinto tipo.
Otros lenguajes (notablemente PL/SQL) incluso admiten sobrecarga de
funciones por nombre de argumento; es decir una clase puede tener varios
métodos con el mismo nombre y número de argumentos de incluso el mismo
tipo, pero con diferentes nombres de argumento. Python no admite ninguno
de estos casos; no hay forma de sobrecarga de funciones. Los métodos se
definen sólo por su nombre, y hay un único método por clase con un nombre
dado. De manera que si una clase sucesora tiene un método __init__,
siempre sustituye al método __init__ de su ancestro,
incluso si éste lo define con una lista de argumentos diferentes. Y se
aplica lo mismo a cualquier otro método.
|
 |
| Guido, el autor original de Python, explica el reemplazo de
métodos así: "Las clases derivadas pueden reemplazar los métodos de sus
clases base. Dado que los métodos no tienen privilegios especiales para
llamar a otros métodos del mismo objeto, un método de una clase base que
llama a otro método definido en la misma clase base, puede en realidad
estar llamando a un método de una clase derivada que la reemplaza (para
programadores de C++: todos los métodos de Python son virtuales a los
efectos)". Si esto no tiene sentido para usted (a mí me confunde
sobremanera), ignórelo. Sólo pensaba que debía comentarlo.
|
 |
| Asigne siempre un valor inicial a todos los atributos de datos de
una instancia en el método __init__. Le quitará horas de depuración más
adelante, en busca de excepciones AttributeError
debido a que está haciendo referencia a atributos sin inicializar (y por
tanto inexistentes).
|
 |
| En las versiones de Python previas a la 2.20, no podía derivar
directamente tipos de datos internos como cadenas, listas y diccionarios.
Para compensarlo, Python proporcionaba clases encapsulantes que imitaban
el comportamiento de estos tipos: UserString,
UserList, y UserDict. Usando una
combinación de métodos normales y especiales, la clase
UserDict hace una excelente imitación de un diccionario. En
Python 2.2 y posteriores, puede hacer que una clase herede directamente
de tipos incorporados como dict. Muestro esto en los ejemplos que
acompañan al libro, en fileinfo_fromdict.py.
|
- 5.6.1. Consultar y modificar elementos
 |
| Cuando se accede a atributos de datos dentro de una clase,
necesitamos calificar el nombre del atributo:
self.atributo. Cuando
llamamos a otros métodos dentro de una clase, necesitamos calificar el
nombre del método:
self.método.
|
- 5.7. Métodos especiales avanzados
 |
| En Java, determinamos si dos variables de cadena referencian la
misma posición física de memoria usando str1 == str2. A
esto se le denomina identidad de objetos, y en
Python se escribe así: str1 is str2. Para comparar
valores de cadenas en Java,
usaríamosstr1.equals(str2); en Python, usaríamos
str1 == str2. Los programadores de Java a los que se
les haya enseñado a creer que el mundo es un lugar mejor porque
== en Java compara la identidad en lugar del valor
pueden tener dificultades ajustándose a la falta de este tipo de
“gotchas” en
Python.
|
 |
| Mientras que otros lenguajes orientados a objeto sólo le permitirán
definir el modelo físico de un objeto (“este objeto tiene un método
GetLength”), los métodos especiales de
Python como __len__ le permiten definir el modelo lógico de un
objeto (“este objeto tiene una longitud”).
|
- 5.8. Presentación de los atributos de clase
 |
| En Java, tanto las variables estáticas (llamadas atributos de
clase en Python) como las variables de instancia (llamadas atributos de
datos en Python) se declaran inmediatamente en la definición de la clase
(unas con la palabra clave static, otras sin ella). En
Python, sólo se pueden definir aquí los atributos de clase; los
atributos de datos se definen en el método __init__.
|
 |
| No hay constantes en Python. Todo puede cambiar si lo intenta con
ahínco. Esto se ajusta a uno de los principios básicos de Python: los
comportamientos inadecuados sólo deben desaconsejarse, no prohibirse. Si
en realidad quiere cambiar el valor de None, puede hacerlo, pero no
venga luego llorando si es imposible depurar su código.
|
- 5.9. Funciones privadas
 |
| En Python, todos los métodos especiales (como __setitem__) y
atributos incorporados (como __doc__) siguen una convención estándar:
empiezan y terminan con dos guiones bajos. No ponga a sus propios métodos
ni atributos nombres así, porque sólo le confudirán a usted (y otros) más
adelante.
|
Capítulo 6. Excepciones y gestión de ficheros
- 6.1. Gestión de excepciones
 |
| Python utiliza try...except para capturar las excepciones y
raise para generarlas. Java y C++ usan
try...catch para capturarlas, y
throw para generarlas.
|
- 6.5. Trabajo con directorios
 |
| Siempre que sea posible, debería usar las funciones de os y
os.path para manipulaciones sobre ficheros, directorios y rutas. Estos
módulos encapsulan lo específico de cada plataforma, de manera
que funciones como os.path.split funcionen en UNIX,
Windows, Mac OS y cualquier otra plataforma en que funcione
Python.
|
Capítulo 7. Expresiones regulares
- 7.4. Uso de la sintaxis {n,m}
 |
| No hay manera de determinar programáticamente si dos expresiones
regulares son equivalentes. Lo mejor que puede hacer es escribir varios
casos de prueba para asegurarse de que se comporta correctamente con todos
los tipos de entrada relevantes. Hablaremos más adelante en este mismo
libro sobre la escritura de casos de prueba.
|
Capítulo 8. Procesamiento de HTML
- 8.2. Presentación de sgmllib.py
 |
| Python 2.0 sufría un fallo debido al que SGMLParser no podía
reconocer declaraciones (no se llamaba nunca a handle_decl), lo que
quiere decir que se ignoraban los DOCTYPE sin advertirlo. Esto quedó
corregido en Python 2.1.
|
 |
| En el IDE ActivePython para Windows puede especificar
argumentos en la línea de órdenes desde el cuadro de diálogo “Run
script”. Si incluye varios argumentos sepárelos con
espacios.
|
- 8.4. Presentación de BaseHTMLProcessor.py
 |
| La especificación de HTML precisa que todo lo que no sea HTML
(como JavaScript para el cliente) debe estar encerrado dentro de
comentarios de HTML, pero no todas las páginas web lo hacen
correctamente (y todos los navegadores modernos hacen la vista gorda en
ese caso). BaseHTMLProcessor no es tan permisivo; si un script no está adecuadamente embebido, será
analizado como si fuera HTML. Por ejemplo, si el script contiene
símbolos "igual" o "menor que", SGMLParser puede entender de
incorrectamente que ha encontrado etiquetas y atributos. SGMLParser
siempre convierte los nombres de etiquetas y atributos a minúsculas, lo
que puede inutilizar el script, y BaseHTMLProcessor siempre encierra
los valores de atributos dentro de comillas dobles (incluso si el
documento original utiliza comillas simples o ningún tipo de comillas), lo
que hará inútil el script con
certeza. Proteja siempre sus script dentro de comentarios de HTML.
|
- 8.5. locals y globals
 |
Python 2.2 introdujo un cambio sutil pero importante que afecta al
orden de búsqueda en los espacios de nombre: los ámbitos anidados. En las
versiones de Python anteriores a la 2.2, cuando hace referencia a una
variable dentro de una función
anidada o función
lambda, Python buscará esa variable en el espacio de la
función actual (anidada o lambda) y después en el espacio de
nombres del módulo. Python 2.2 buscará la variable en el espacio de
nombres de la función actual (anidada o lambda),
después en el espacio de su función madre, y luego en
el espacio del módulo. Python 2.1 puede funcionar de ambas maneras; por
omisión lo hace como Python 2.0, pero puede añadir la siguiente línea al
código al principio de su módulo para hacer que éste funcione como
Python 2.2:
from __future__ import nested_scopes |
 |
| Puede obtener dinámicamente el valor de variables arbitrarias usando
las funciones locals y globals, proporcionando el nombre de la
variable en una cadena. Esto imita la funcionalidad de la función getattr, que le permite acceder a
funciones arbitrarias de forma dinámica proporcionando el nombre de la
función en una cadena.
|
- 8.6. Cadenas de formato basadas en diccionarios
 |
| Usar cadenas de formato basadas en diccionarios con locals es una
manera conveniente de hacer más legibles expresiones de cadenas de
formato, pero tiene un precio. Llamar a locals tiene una ligera influencia
en el rendimiento, ya que locals construye una
copia del espacio de nombres local.
|
Capítulo 9. Procesamiento de XML
- 9.2. Paquetes
 |
| Un paquete es un directorio que contiene el fichero especial
__init__.py. El fichero __init__.py define los atributos y métodos
del paquete. No tiene por qué definir nada; puede ser un fichero vacío,
pero ha de existir. Pero si no existe __init__.py el directorio es
sólo eso, un directorio, no un paquete, y no puede ser importado o
contener módulos u otros paquetes.
|
- 9.6. Acceso a atributos de elementos
 |
| Esta sección puede ser un poco confusa, debido a que la terminología
se solapa. Los elementos de documentos XML tienen atributos, y los
objetos de Python también. Cuando analizamos un documento XML
obtenemos un montón de objetos de Python que representan todas las
partes del documento XML, y algunos de estos objetos de Python
representan atributos de los elementos de XML. Pero los objetos (de
Python) que representan los atributos (de XML) también tienen
atributos (de Python), que se usan para acceder a varias partes del
atributo (de XML) que representa el objeto. Le dije que era confuso.
Estoy abierto a sugerencias para distinguirlos con más claridad.
|
 |
| Al igual que un diccionario, los atributos de un elemento XML no
tienen orden. Puede que los atributos estén listados
en un cierto orden en el documento XML original, y puede
que los objetos Attr estén listados en un cierto
orden cuando se convierta el documento XML en objetos de Python, pero
estos órdenes son arbitrarios y no deberían tener un significado especial.
Siempre debería acceder a los atributos por su nombre, como claves de un
diccionario.
|
Capítulo 10. Scripts y flujos
Capítulo 11. Servicios Web HTTP
- 11.6. Tratamiento de Last-Modified y ETag
 |
| En estos ejemplos el servidor HTTP ha respondido tanto a la cabecera
Last-Modified como a ETag, pero no todos los servidores lo hacen. Como
cliente de un servicio web debería estar preparado para usar ambos, pero
debe programar de forma defensiva por si se da el caso de que el servidor
sólo trabaje con uno de ellos, o con ninguno.
|
Capítulo 12. Servicios web SOAP
Capítulo 13. Pruebas unitarias (Unit Testing)
Capítulo 14. Programación Test-First
- 14.3. roman.py, fase 3
 |
| La cosa más importante que le puede decir una prueba unitaria
exhaustiva es cuándo debe dejar de programar. Cuando una función pase
todas sus pruebas unitarias, deje de programarla. Cuando un módulo pase
todas sus pruebas unitarias, deje de programar en él.
|
- 14.5. roman.py, fase 5
 |
| Cuando hayan pasado todas sus pruebas, deje de programar. |
Capítulo 15. Refactorización
- 15.3. Refactorización
 |
| Siempre que vaya a usar una expresión regular más de una vez,
debería compilarla para obtener un objeto patrón y luego llamar
directamente a los métodos del patrón.
|
Capítulo 16. Programación Funcional
Capítulo 17. Funciones dinámicas
Capítulo 18. Ajustes de rendimiento
- 18.2. Uso del módulo timeit
 |
| Podemos usar el módulo timeit desde la línea de órdenes para probar
un programa de Python que ya exista, sin modificar el código. Vea http://docs.python.org/lib/node396.html si desea documentación
sobre las opciones para línea de órdenes.
|
 |
| El módulo timeit sólo sirve de algo si ya sabe qué parte de su
código quiere optimizar. Si tiene un programa grande escrito en Python
y no sabe dónde tiene los problemas de rendimiento, pruebe
el módulo
hotshot. |