| You are here: Inicio > Inmersión en Python > Repaso en 5 minutos | << >> | ||||
Inmersión en PythonPython de novato a experto |
|||||
Capítulo 1. Instalación de Python
La primera cosa que debe hacer con Python es instalarlo. ¿O no?
En Windows debe hacer un par de elecciones antes de instalar Python.
En Mac OS X cuenta con dos opciones para instalar Python: instalarlo o no instalarlo. Probablemente quiera instalarlo.
Mac OS 9 no incluye una versión de Python pero instalarla es muy sencillo, y sólo hay una opción.
Descarge el último RPM de Python yendo a http://www.python.org/ftp/python/ y escogiendo el número de versión más alto en la lista, y dentro de ahí, el directorio rpms/. Entonces descargue el RPM con el número de versión más alto. Puede instalarlo con la orden rpm, como se muestra aquí:
Si tiene la suerte de usar Debian GNU/Linux, instale Python usando la orden apt.
Si prefiere compilar el código fuente, puede descargar el de Python desde http://www.python.org/ftp/python/. Escoja el número de versión más alto, descargue el fichero .tgz, y ejecute entonces el ritual habitual de configure, make, make install.
Ahora que ya ha instalado Python, ¿qué es este intérprete interactivo que está ejecutando?
Ahora debería tener una versión de Python instalada que funcione.
Capítulo 2. Su primer programa en Python
Aquí tiene un programa en Python, completo y funcional.
Python tiene funciones como la mayoría de otros lenguajes, pero no dispone de ficheros de cabeceras como C++ o secciones interface/implementation como tiene Pascal. Cuando necesite una función, limítese a declararla, como aquí:
Puede documentar una función en Python proporcionando una cadena de documentación.
Una función es un objeto, igual que todo lo demás en Python.
Las funciones de Python no tienen begin o end explícitos, ni llaves que marquen dónde empieza o termina su código. El único delimitador son dos puntos (:) y el sangrado del propio código.
Los módulos de Python son objetos y tienen varios atributos útiles. Puede usar este hecho para probar sus módulos de forma sencilla a medida que los escribe. Aquí tiene un ejemplo que usa el truco de if __name__.
Capítulo 3. Tipos de dato nativos
Uno de los tipos incorporados de Python es el diccionario, que define relaciones uno a uno entre claves y valores.
Las listas son el caballo de tiro de Python. Si su única experiencia con listas son los array de Visual Basic o (dios no lo quiera) los datastore de Powerbuilder, prepárese para las listas de Python.
Una tupla es una lista inmutable. Una tupla no puede cambiar de ninguna manera una vez creada.
Python tiene variables locales y globales como casi todo el resto de lenguajes, pero no tiene declaración explícita de variables. Las variables cobran existencia al asignársele un valor, y se destruyen automáticamente al salir de su ámbito.
Python admite dar formato a valores dentro de cadenas. Aunque esto puede incluir expresiones muy complicadas, el uso más básico es insertar valores dentro de una cadena con el sustituto %s.
Una de las características más potentes de Python es la lista por comprensión (list comprehension), que proporciona una forma compacta de inyectar una lista en otra aplicando una función a cada uno de sus elementos.
Tenemos una lista de pares clave-valor de forma clave=valor, y queremos juntarlos en una sola cadena. Para juntar una lista de cadenas en una sola, usaremos el método join de un objeto de cadena.
El programa odbchelper.py y su salida debería tener total sentido ahora.
Capítulo 4. El poder de la introspección
Aquí hay un programa en Python completo y funcional. Debería poder comprenderlo en gran parte sólo observándolo. Las líneas numeradas ilustran conceptos cubiertos en el Capítulo 2, Su primer programa en Python. No se preocupe si el resto del código le parece inquietante; aprenderá todo sobre él en este capítulo.
Python permite que los argumentos de las funciones tengan valores por omisión; si se llama a la función sin el argumento, éste toma su valor por omisión. Además, los argumentos pueden especificarse en cualquier orden indicando su nombre. Los procedimientos almacenados en SQL Server Transact/SQL pueden hacer esto; si es usted un gurú de los scripts en SQL Server, puede saltarse esta parte.
Python cuenta con un pequeño conjunto de funciones incorporadas enormemente útiles. Todas las demás funciones están repartidas en módulos. Esto es una decisión consciente de diseño, para que el núcleo del lenguaje no se hinche como en otros lenguajes de script (cof cof, Visual Basic).
Ya sabe usted que las funciones de Python son objetos. Lo que no sabe es que se puede obtener una referencia a una función sin necesidad de saber su nombre hasta el momento de la ejecución, utilizando la función getattr.
Como ya sabe, Python tiene potentes capacidades para convertir una lista en otra por medio de las listas por comprensión (Sección 3.6, “Inyección de listas (mapping)”). Esto puede combinarse con un mecanismo de filtrado en el que se van a tomar algunos elementos de la lista mientras otros se pasan por alto.
En Python and y or realizan las operaciones de lógica booleana como cabe esperar, pero no devuelven valores booleanos; devuelven uno de los valores que están comparando.
Python admite una interesante sintaxis que permite definir funciones mínimas, de una línea, sobre la marcha. Tomada de Lisp, se trata de las denominadas funciones lambda, que pueden utilizarse en cualquier lugar donde se necesite una función.
La última línea de código, la única que no hemos desmenuzado todavía, es la que hace todo el trabajo. Pero el trabajo ya es fácil, porque todo lo que necesitamos está dispuesto de la manera en que lo necesitamos. Las fichas de dominó están en su sitio; lo que queda es golpear la primera.
El programa apihelper.py y su salida deberían entenderse ya perfectamente.
Capítulo 5. Objetos y orientación a objetos
Aquí tiene un programa en Python completo y funcional. Lea las cadenas de documentación del módulo, las clases, y las funciones para obtener una idea general de lo que hace el programa y cómo funciona. Como de costumbre, no se preocupe por lo que no entienda; para eso está el resto del capítulo.
Python tiene dos maneras de importar módulos. Ambas son útiles, y debe saber cuándo usar cada cual. Una de las maneras, import módulo, ya la hemos visto en Sección 2.4, “Todo es un objeto”. La otra hace lo mismo, pero tiene diferencias sutiles e importantes.
Python está completamente orientado a objetos: puede definir sus propias clases, heredar de las que usted defina o de las incorporadas en el lenguaje, e instanciar las clases que haya definido.
La instanciación de clases en Python es trivial. Para instanciar una clase, simplemente invoque a la clase como si fuera una función, pasando los argumentos que defina el método __init__. El valor de retorno será el objeto recién creado.
Como ha podido ver, FileInfo es una clase que actúa como un diccionario. Para explorar esto un poco más, veamos la clase UserDict del módulo UserDict, que es el ancestro de la clase FileInfo . No es nada especial; la clase está escrita en Python y almacenada en un fichero .py, igual que cualquier otro código Python. En particular, está almacenada en el directorio lib de su instalación de Python.
Además de los métodos normales, existe un cierto número de métodos especiales que pueden definir las clases de Python. En lugar de llamarlos directamente desde su código (como los métodos normales), los especiales los invoca Python por usted en circunstancias particulares o cuando se use una sintaxis específica.
Python tiene más métodos especiales aparte de __getitem__ y __setitem__. Algunos de ellos le permiten emular funcionalidad que puede que aún ni conozca.
Ya conoce los atributos de datos, que son variables que pertenecen a una instancia específica de una clase. Python también admite atributos de clase, que son variables que pertenecen a la clase en sí.
Al contrario que en muchos otros lenguajes, la privacidad de una función, método o atributo de Python viene determinada completamente por su nombre.
Esto es todo en cuanto a trucos místicos. Verá una aplicación en el mundo real de métodos especiales de clase en Capítulo 12, que usa getattr para crear un proxy a un servicio remoto por web.
Capítulo 6. Excepciones y gestión de ficheros
Como muchos otros lenguajes de programación, Python gestiona excepciones mediante bloques try...except.
Python incorpora una función, open, para abrir ficheros de un disco. open devuelve un objeto de fichero, que tiene métodos y atributos para obtener información sobre el fichero abierto y manipularlo.
Como la mayoría de los otros lenguajes, Python cuenta con bucles for. La única razón por la que no los ha visto antes es que Python es bueno en tantas otras cosas que no los ha necesitado hasta ahora con tanta frecuencia.
Los módulos son objetos, como todo lo demás en Python. Una vez importado, siempre puede obtener una referencia a un módulo mediante el diccionario global sys.modules.
El módulo os.path tiene varias funciones para manipular ficheros y directorios. Aquí queremos manipular rutas y listar el contenido de un directorio.
Una vez más, todas las piezas de dominó están en su lugar. Ha visto cómo funciona cada línea de código. Ahora retrocedamos y veamos cómo encaja todo.
El programa fileinfo.py que presentamos en el Capítulo 5 debería ahora tener todo el sentido del mundo.
Capítulo 7. Expresiones regulares
Si lo que intentamos hacer se puede realizar con las funciones de cadenas, debería usarlas. Son rápidas y simples, y sencillas de entender, y todo lo bueno que se diga sobre el código rápido, simple y legible, es poco. Pero si se encuentra usando varias funciones de cadenas diferentes junto con sentencias if para manejar casos especiales, o si las tiene que combinar con split y join y listas por comprensión de formas oscuras e ilegibles, puede que deba pasarse a las expresiones regulares.
Esta serie de ejemplos la inspiró un problema de la vida real que surgió en mi trabajo diario hace unos años, cuando necesité limpiar y estandarizar direcciones de calles exportadas de un sistema antiguo antes de importarlo a un nuevo sistema (vea que no me invento todo esto; es realmente útil). Este ejemplo muestra la manera en que me enfrenté al problema.
Seguramente ha visto números romanos, incluso si no los conoce. Puede que los haya visto en copyrights de viejas películas y programas de televisión (“Copyright MCMXLVI” en lugar de “Copyright 1946”), o en las placas conmemorativas en bibliotecas o universidades (“inaugurada en MDCCCLXXXVIII” en lugar de “inaugurada en 1888”). Puede que incluso los haya visto en índices o referencias bibliográficas. Es un sistema de representar números que viene de los tiempos del antiguo imperio romano (de ahí su nombre).
En la sección anterior, tratamos con un patrón donde el mismo carácter podía repetirse hasta tres veces. Hay otra manera de expresar esto con expresiones regulares, que algunas personas encuentran más legible. Primero mire el método que hemos usado ya en los ejemplos anteriores.
Hasta ahora sólo hemos tratado con lo que llamaremos expresiones regulares “compactas”. Como ha visto, son difíciles de leer, e incluso si uno sabe lo que hace, eso no garantiza que seamos capaces de comprenderlas dentro de seis meses. Lo que estamos necesitando es documentación en línea.
Por ahora se ha concentrado en patrones completos. Cada patrón coincide, o no. Pero las expresiones regulares son mucho más potentes que eso. Cuando una expresión regular coincide, puede extraer partes concretas. Puede saber qué es lo que causó la coincidencia.
Ésta es sólo la minúscula punta del iceberg de lo que pueden hacer las expresiones regulares. En otras palabras, incluso aunque esté completamente saturado con ellas ahora mismo, créame, todavía no ha visto nada.
Capítulo 8. Procesamiento de HTML
A menudo veo preguntas en comp.lang.python parecidas a “¿Cómo puedo obtener una lista de todas las [cabeceras|imágenes|enlaces] en mi documento HTML?” “¿Cómo analizo/traduzco/manipulo el texto de mi documento HTML pero sin tocar las etiquetas?” “¿Cómo puedo añadir/eliminar/poner comillas a los atributos de mis etiquetas HTML de una sola vez?” Este capítulo responderá todas esas preguntas.
El procesamiento de HTML se divide en tres pasos: obtener del HTML sus partes constitutivas, manipular las partes y reconstruirlas en un documento HTML. El primero paso lo realiza sgmllib.py, una parte de la biblioteca estándar de Python.
Para extraer datos de documentos HTML derivaremos la clase SGMLParser y definiremos métodos para cada etiqueta o entidad que queramos capturar.
SGMLParser no produce nada por sí mismo. Analiza, analiza y analiza, e invoca métodos por cada cosa interesante que encuentra, pero los métodos no hacen nada. SGMLParser es un consumidor de HTML: toma un HTML y lo divide en trozos pequeños y estructurados. Como vio en la sección anterior, puede derivar SGMLParser para definir clases que capturen etiquetas específicas y produzcan cosas útiles, como una lista de todos los enlaces en una página web. Ahora llevará esto un paso más allá, definiendo una clase que capture todo lo que SGMLParser le lance, reconstruyendo el documento HTML por completo. En términos técnicos, esta clase será un productor de HTML.
Hagamos por un minuto un inciso entre tanto procesamiento de HTML y hablemos sobre la manera en que Python gestiona las variables. Python incorpora dos funciones, locals y globals, que proporcionan acceso de tipo diccionario a las variables locales y globales.
Hay una forma alternativa de dar formato a cadenas que usa diccionarios en lugar de tuplas de valores.
Una pregunta habitual en comp.lang.python es “Tengo unos documentos HTML con valores de atributos sin comillas, y quiero corregirlos todos. ¿Cómo puedo hacerlo?”[9] (Generalmente sucede cuando un jefe de proyecto que ha abrazado la religión HTML-es-un-estándar se une a un proyecto y proclama que todas las páginas deben pasar el validador de HTML. Los valores de atributos sin comillas son una violación habitual del estándar HTML). Cualquiera que sea la razón, es fácil corregir el problema de los valores de atributo sin comillas alimentando a BaseHTMLProcessor con HTML.
Dialectizer es una descendiente sencilla (y tonta) de BaseHTMLProcessor. Hace una serie de sustituciones sobre bloques de texto, pero se asegura de que cualquier cosa dentro de un bloque <pre>...</pre> pase sin alteraciones.
Es hora de darle buen uso a todo lo que ha aprendido hasta ahora. Espero que haya prestado atención.
Python le proporciona una herramienta potente, sgmllib.py, para manipular HTML volviendo su estructura en un modelo de objeto. Puede usar esta herramienta de diferentes maneras.
Capítulo 9. Procesamiento de XML
Hay dos maneras básicas de trabajar con XML. Una se denomina SAX (“Simple API for XML”), y funciona leyendo un poco de XML cada vez, invocando un método por cada elemento que encuentra (si leyó Capítulo 8, Procesamiento de HTML, esto debería serle familiar, porque es la manera en que trabaja el módulo sgmllib). La otra se llama DOM (“Document Object Model”), y funciona leyendo el documento XML completo para crear una representación interna utilizando clases nativas de Python enlazadas en una estructura de árbol. Python tiene módulos estándar para ambos tipos de análisis, pero en este capítulo sólo trataremos el uso de DOM.
Analizar un documento XML es algo muy sencillo: una línea de código. Sin embargo, antes de llegar a esa línea de código hará falta dar un pequeño rodeo para hablar sobre los paquetes.
Como iba diciendo, analizar un documento XML es muy sencillo: una línea de código. A dónde ir partiendo de eso es cosa suya.
Unicode es un sistema para representar caracteres de todos los diferentes idiomas en el mundo. Cuando Python analiza un documento XML, todos los datos se almacenan en memoria como unicode.
Recorrer un documento XML saltando por cada nodo puede ser tedioso. Si está buscando algo en particular, escondido dentro del documento XML, puede usar un atajo para encontrarlo rápidamente: getElementsByTagName.
Los elementos de XML pueden tener uno o más atributos, y es increíblemente sencillo acceder a ellos una vez analizado el documento XML.
Bien, esto era el material más duro sobre XML. El siguiente capítulo continuará usando estos mismos programas de ejemplo, pero centrándose en otros aspectos que hacen al programa más flexible: uso de flujos[12] para proceso de entrada, uso de getattr para despachar métodos, y uso de opciones en la línea de órdenes para permitir a los usuarios reconfigurar el programa sin cambiar el código.
Uno de los puntos más fuertes de Python es su enlace dinámico, y un uso muy potente del enlace dinámico es el objeto tipo fichero.
Los usuarios de UNIX ya estarán familiarizados con el concepto de entrada estándar, salida estándar y salida estándar de error. Esta sección es para los demás.
kgp.py emplea varios trucos que pueden o no serle útiles en el procesamiento de XML. El primero aprovecha la estructura consistente de los documentos de entrada para construir una caché de nodos.
Otra técnica útil cuando se analizan documentos XML es encontrar todos los elementos que sean hijos directos de un elemento en particular. Por ejemplo, en los ficheros de gramáticas un elemento ref puede tener varios elementos p, cada uno de los cuales puede contener muchas cosas, incluyendo otros elementos p. Queremos encontrar sólo los elementos p que son hijos de ref, no elementos p que son hijos de otros elementos p.
El tercer consejo útil para procesamiento de XML implica separar el código en funciones lógicas, basándose en tipos de nodo y nombres de elemento. Los documentos XML analizados se componen de varios tipos de nodos que representa cada uno un objeto de Python. El nivel raíz del documento en sí lo representa un objeto Document. El Document contiene uno o más objetos Element (por las etiquetas XML), cada uno de los cuales contiene otros objetos Element, Text (para zonas de texto) o Comment (para los comentarios). Python hace sencillo escribir algo que separe la lógica por cada tipo de nodo.
Python admite la creación de programas que se pueden ejecutar desde la línea de órdenes, junto con argumentos y opciones tanto de estilo corto como largo para especificar varias opciones. Nada de esto es específico al XML, pero este script hace buen uso del tratamiento de la línea de órdenes, así que parece buena idea mencionarlo.
Ha cubierto mucho terreno. Volvamos atrás y comprobemos cómo se unen todas las piezas.
Python incluye bibliotecas potentes para el análisis y la manipulación de documentos XML. minidom toma un fichero XML y lo convierte en objetos de Python, proporcionando acceso aleatorio a elementos arbitrarios. Aún más, este capítulo muestra cómo se puede usar Python para crear un script de línea de órdenes, completo con sus opciones, argumentos, gestión de errores, e incluso la capacidad de tomar como entrada el resultado de un programa anterior mediante una tubería.
Capítulo 11. Servicios Web HTTP
Hemos aprendido cosas sobre procesamiento de HTML y de XML, y por el camino también vio cómo descargar una página web y analizar XML de una URL, pero profundicemos algo más en el tema general de los servicios web HTTP.
Digamos que quiere descargar un recurso mediante HTTP, tal como un feed sindicado Atom. Pero no sólo queremos descargarlo una vez; queremos descargarlo una y otra vez, cada hora, para obtener las últimas noticias de un sitio que nos ofrece noticias sindicadas. Hagámoslo primero a la manera sucia y rápida, y luego veremos cómo hacerlo mejor.
Hay cinco características importantes de HTTP que debería tener en cuenta.
Primero vamos a activar las características de depuración de la biblioteca de HTTP de Python y veamos qué está viajando por los cables. Esto será útil durante el capítulo según añadamos características.
El primer paso para mejorar nuestro cliente de servicios web HTTP es identificarnos adecuadamente con User-Agent. Para hacerlo nos hace falta pasar de la urllib básica y zambullirnos en urllib2.
Ahora que sabe cómo añadir cabeceras HTTP personalizadas a la consulta al servicio web, veamos cómo añadir la funcionalidad de las cabeceras Last-Modified y ETag.
Puede admitir redirecciones permanentes y temporales usando un tipo diferente de manipulador de URL.
La última característica importante de HTTP que queremos tratar es la compresión. Muchos servicios web tienen la capacidad de enviar los datos comprimidos, lo que puede rebajar en un 60% o más la cantidad de datos a enviar. Esto se aplica especialmente a los servicios web XML, ya que los datos XML se comprimen bastante bien.
Ya hemos visto todas las partes necesarias para construir un cliente inteligente de servicios web HTTP. Ahora veamos cómo encaja todo.
openanything.py y sus funciones deberían tener sentido ahora.
Capítulo 12. Servicios web SOAP
Usted usa Google, ¿verdad? Es un motor de búsquedas popular. ¿Ha deseado alguna vez poder acceder a los resultados de búsquedas de Google de forma programática? Ahora puede. Aquí tiene un programa que busca en Google desde Python.
Al contrario que el resto de código de este libro, este capítulo se apoya en bibliotecas que no se incluyen preinstaladas en Python.
El corazón de SOAP es la capacidad de invocar funciones remotas. Hay varios servidores SOAP de acceso público que proporcionan funciones simples con propósito de demostración.
Las bibliotecas de SOAP proporcionan una manera sencilla de comprobar lo que está sucediendo tras la escena.
La clase SOAPProxy hace proxy de llamadas locales a métodos y las convierte de forma transparente en invocaciones a métodos SOAP remotos. Como ya ha visto esto lleva mucho trabajo, y el SOAPProxy lo hace rápida y transparentemente. Lo que no hace es proporcionarnos ningún tipo de método para introspección.
Como muchas otras cosas en el campo de los servicios web, WSDL tiene una historia larga y veleidosa, llena de conflictos e intrigas políticas. Me saltaré toda esta historia ya que me aburre hasta lo indecible. Hay otros estándares que intentaron hacer cosas similares, pero acabó ganando WSDL así que aprendamos cómo usarlo.
Volvamos finalmente al ejemplo de código que vio al comienzo de este capítulo, que hace algo más útil y excitante que obtener la temperatura.
Por supuesto, el mundo de los servicios web SOAP no es todo luz y felicidad. Algunas veces las cosas van mal.
Los servicios web SOAP son muy complicados. La especificación es muy ambiciosa e intenta cubrir muchos casos de uso de servicios web. Este capítulo ha tocado algunos de los casos más sencillos.
Capítulo 13. Pruebas unitarias (Unit Testing)
En capítulos interiores, se “sumergió” dando un vistazo rápido al código e intentándo comprenderlo lo más rápidamente posible. Ahora que tiene bastante Python a sus espaldas, vamos a retroceder y ver los pasos que se dan antes de escribir el código.
Ahora que ya hemos definido completamente el comportamiento esperado de nuestras funciones de conversión, vamos a hacer algo un poco inesperado: vamos a escribir una batería de pruebas que ponga estas funciones contra las cuerdas y se asegure de que se comportan de la manera en que queremos. Ha leído bien: va a escribir código que pruebe código que aún no hemos escrito.
Ésta es la batería de pruebas completa para las funciones de conversión de números romanos, que todavía no están escritas, pero en el futuro estarán en roman.py. No es inmediatamente obvio cómo encaja todo esto; ninguna de las clases o funciones hace referencia a las otras. Hay buenas razones para esto, como veremos en breve.
La parte más fundamental de una prueba unitaria es la construcción de casos de prueba individuales. Un caso de prueba responde a una única pregunta sobre el código que está probando.
No es suficiente probar que las funciones tienen éxito cuando se les pasa valores correctos; también debe probar que fallarán si se les da una entrada incorrecta. Y no sólo cualquier tipo de fallo; deben fallar de la manera esperada.
A menudo se encontrará con que un código unitario contiene un conjunto de funciones recíprocas normalmente en forma de funciones de conversión en que una convierte de A a B y la otra de B a A. En estos casos es útil crear “pruebas de cordura” (sanity checks para asegurarse de que puede convertir de A a B y de vuelta a A sin perder precisión, incurrir en errores de redondeo o encontrar ningún otro tipo de fallo.
Capítulo 14. Programación Test-First
Ahora que están terminadas las pruebas unitarias, es hora de empezar a escribir el código que intentan probar esos casos de prueba. Vamos a hacer esto por etapas, para que pueda ver fallar todas las pruebas, y verlas luego pasar una por una según llene los huecos de roman.py.
Ahora que tenemos preparado el marco de trabajo del módulo roman, es hora de empezar a escribir código y pasar algunos casos de prueba.
Ahora que toRoman se comporta correctamente con entradas correctas (enteros del 1 al 3999), es hora de hacer que se comporte correctamente con entradas incorrectas (todo lo demás).
Ahora que está hecha toRoman es hora de empezar a programar fromRoman. Gracias a la rica estructura de datos que relaciona cada número romano a un valor entero, no es más difícil que la función toRoman.
Ahora que fromRoman funciona adecuadamente con entradas correctas es el momento de encajar la última pieza del puzzle: hacer que funcione adecuadamente con entradas incorrectas. Esto implica buscar una manera de mirar una cadena y determinar si es un número romano válido. Esto es inherentemente más difícil que validar entrada numérica en toRoman, pero tenemos una herramienta potente a nuestra disposición: expresiones regulares.
A pesar de nuestros mejores esfuerzos para escribir pruebas unitarias exhaustivas, los fallos aparecen. ¿A qué me refiero con “un fallo”? Un fallo es un caso de prueba que aún no hemos escrito.
A pesar de nuestros mejores esfuerzos para agarrar a nuestros clientes y extraerles requisitos exactos usando el dolor de cosas horribles que impliquen tijeras y cera caliente, los requisitos cambiarán. La mayoría de los clientes no sabe lo que quiere hasta que lo ve, e incluso si lo saben, no son buenos explicando qué quieren de forma lo suficientemente precisa como para que sea útil. Así que prepárese para actualizar los casos de prueba según cambien los requisitos.
Lo mejor de las pruebas unitarias exhaustivas no es la sensación que le queda cuando todos los casos de prueba terminan por pasar, o incluso la que le llega cuando alguien le acusa de romper su código y usted puede probar realmente que no lo hizo. Lo mejor de las pruebas unitarias es que le da la libertad de refactorizar sin piedad.
El lector inteligente leería la sección anterior y la llevaría al siguiente nivel. El mayor dolor de cabeza (y lastre al rendimiento) en el programa tal como está escrito es la expresión regular, que precisamos porque no tenemos otra manera de hacer disección de un número romano. Pero sólo hay 5000; ¿por qué no podemos generar una tabla de búsqueda una vez y luego limitarnos a leerla? La idea se vuelve aún mejor cuando nos damos cuenta de que no necesitamos usar expresiones regulares para nada. Mientras creamos la tabla para convertir enteros a números romanos podemos construir la tabla inversa que convierta romanos a enteros.
Las pruebas unitarias son un concepto potente que, si se implementa adecuadamente, puede tanto reducir el coste de mantenimiento como incrementar la flexibilidad en cualquier proyecto a largo plazo. Es importante también entender que las pruebas unitarias no son la panacea, un Resolutor Mágico de Problemas o una bala de plata. Escribir buenos casos de prueba es duro, y mantenerlos actualizados necesita disciplina (especialmente cuando los clientes están gritando que quieren arreglos para fallos críticos). Las pruebas unitarias no sustituyen otras formas de comprobación, incluyendo las pruebas funcionales, las de integración y las de aceptación del cliente. Pero son factibles y funcionan, y una vez que las ha visto trabajando uno se pregunta cómo ha podido pasar sin ellas hasta ahora.
Capítulo 16. Programación Funcional
En Capítulo 13, Pruebas unitarias (Unit Testing) aprendimos la filosofía de las pruebas unitarias. En Capítulo 14, Programación Test-First implementamos paso a paso una prueba unitaria en Python. En Capítulo 15, Refactorización vimos cómo las pruebas unitarias hacen más sencilla la refactorización a gran escala. Este capítulo se apoyará en esos programas de ejemplo, pero aquí nos centraremos en técnicas más avanzadas específicas de Python, en lugar de en las pruebas unitarias en sí.
A veces es útil, cuando ejecutamos scripts de Python desde la línea de órdenes, saber dónde está situado en el disco el script que está en marcha.
Ya está familiarizado con el uso de listas por comprensión para filtrar listas. Hay otra manera de conseguir lo mismo que algunas personas consideran más expresiva.
Ya está familiarizado con el uso de listas por comprensión para hacer corresponder una a otra. Hay otra manera de conseguir lo mismo usando la función incorporada map. Funciona muy parecido a filter.
Ahora es probable que esté rascándose la cabeza preguntándose por qué es mejor esto que usar bucles for e invocar directamente a las funciones. Y es una pregunta perfectamente válida. En su mayoría es una cuestión de perspectiva. Usar map y filter le fuerza a centrar sus pensamientos sobre los datos.
Bien, basta de filosofar. Hablemos sobre la importación dinámica de módulos.
Hemos aprendido suficiente para hacer disección de las primeras siete líneas del código de ejemplo de este capítulo: lectura de un directorio e importación de los módulos seleccionados dentro de él.
El programa regression.py y sus salidas deberían tener perfecto sentido.
Capítulo 17. Funciones dinámicas
Quiero hablar sobre los sustantivos en plural. También sobre funciones que devuelven otras funciones, expresiones regulares avanzadas y generadores. Los generadores son nuevos en Python 2.3. Pero primero hablemos sobre cómo hacer nombres en plural[20].
Así que estamos mirando las palabras, que al menos en inglés son cadenas de caracteres. Y tenemos reglas que dicen que debe encontrar diferentes combinaciones de caracteres y luego hacer cosas distintas con ellos. Esto suena a trabajo para las expresiones regulares.
Ahora vamos a añadir un nivel de abstracción. Empezamos definiendo una lista de reglas: si pasa esto, haz aquello, si no pasa a la siguiente regla. Compliquemos temporalmente parte del programa para poder simplificar otra.
No es realmente necesario definir una función aparte para cada regla de coincidencia y modificación. Nunca las invocamos directamente; las definimos en la lista rules y las llamamos a través suya. Vamos a reducir el perfil de la definición de las reglas haciéndolas anónimas.
Eliminemos la duplicación del código para que sea más sencillo definir nuevas reglas.
Hemos eliminado casi todo el código duplicado y añadido suficientes abstracciones para que las reglas de pluralización se definan en una lista de cadenas. El siguiente paso lógico es tomar estas cadenas y ponerlas en un fichero aparte, donde se puedan mantener de forma separada al código que las usa.
Ahora está listo para que le hable de generadores.
En este capítulo hemos hablado sobre varias técnicas avanzadas diferentes. No todas son apropiadas para cada situación.
Capítulo 18. Ajustes de rendimiento
Hay tantas trampas en el camino a optimizar el código, que es difícil saber dónde empezar.
La cosa más importante que debe saber sobre optimización de código en Python es que no debería escribir su propia función de cronometraje.
Lo primero que comprueba la función Soundex es si la entrada es una cadena de letras que no esté vacía. ¿Cual es la mejor manera de hacerlo?
El segundo paso del algoritmo Soundex es convertir los caracteres en dígitos según un patrón específico. ¿Cual es la mejor manera de hacer esto?
El tercer paso en el algoritmo Soundex es eliminar dígitos consecutivos duplicados. ¿Cual es la mejor manera de hacerlo?
El último paso del algoritmo Soundex es rellenar los resultados cortos con ceros y truncar los largos. ¿Cual es la mejor manera de hacerlo?
Este capítulo ha ilustrado varios aspectos importantes del ajuste de rendimiento en Python y en general.
<< Lecturas complementarias |
| | |
Trucos y consejos >> |