El Zen de Python: Guía para Pythonistas

Tim Peters, activo colaborador del Python por mucho tiempo, escribió hace algunos años una colección de diecinueve principios básicos que constituyen el mejor resumen que se haya escrito sobre la filosofía que respalda el desarrollo y empleo de Python. Incluidos por defecto en la librería estándar de Python como un Huevo de Pascua, estos principios pueden ser visualizados ejecutando:

>>> import this

Esta sentencia muestra en nuestras pantallas el lo siguiente:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren’t special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one– and preferably only one –obvious way to do it.

Although that way may not be obvious at first unless you’re Dutch.

Now is better than never.

Although never is often better than *right* now.

If the implementation is hard to explain, it’s a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea — let’s do more of those!

Hurguemos un poco en estos principios básicos, pero esta vez en la lengua de Cervantes.

Bello es mejor que feo

Este primer principio refleja algo esencial en la psicología humana, siempre las cosas bien hechas y con rasgos estéticos agradables a los sentidos humanos, son mejor aceptadas que aquella que resultan feas o que de alguna manera causan cierto disgusto o perturbación. Un código bien escrito, coherente, legible y sencillo, que además, se acoja a las convenciones y expresiones idiomáticas del lenguaje; siempre es mejor que un código mal escrito, enredado y poco legible. El empleo de palabras en idioma inglés en lugar de símbolos de puntuación, la indentación en lugar de llaves, el empleo de argumentos keywords en la llamada a funciones, entre otros, son ejemplos claros del contenido detrás de este principio.

Explicito es mejor que implícito

Si hay algo que necesitamos decir, no lo dejemos entre líneas o escondido en sentencias complejas y llenas entuertos, expresémoslo clara y directamente. Hagamos que se entienda lo que queremos decir y nos ahorraremos malos entendidos e interpretaciones erróneas.

Por ejemplo:

sr = sch(2, 100, ‘Juan Pérez’, c)

search_result = search(start=2, stop=100, sub_string=‘Juan Pérez’, base_string=c)

De las dos sentencias anteriores, ¿cuál le resulta más comprensible?

Este ejemplo también refleja el punto expresado en el principio anterior.

Simple es mejor que complejo

Esto es algo elemental, cualquiera que se la disciplina del saber humano a que nos enfrentemos, una solución simple y elegante, siempre será mucho mejor que una solución compleja y difícil de comprender. Este principio no es más que el reflejo hacia adentro del desarrollo de Python del conocido principio KISS (Keep It Simple, Stupid) que todos hemos visto alguna vez.

Este principio es muy importante para aquellos que se dedican al desarrollo de frameworks y librerías, pues deben asegurarse de que la interfaz diseñada sea lo más clara y sencilla posible, para que sus usuarios puedan hacer uso de ella intuitivamente.

A manera de ilustración veamos el código siguiente:

>>> digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> for i in range(len(digits)):

... print(i)

Este código, además de ser bastante ineficiente y poco pythonico, es bien enredado. De seguro, si lo simplificamos un poco se verá y funcionará mucho mejor:

>>> for digit in digits:

... print(digit)

Esta variante, además de ser más simple y legible, es también más rápida y eficiente.

Complejo es mejor que complicado

Se trata en esta ocasión de darle continuidad al principio anterior. Con frecuencia nos sucede que no podemos lograr que nuestra solución sea sencilla y clara por determinadas restricciones del sistema que estemos diseñando o por restricciones del entorno o por lo que sea, pero debemos tratar de evitar que nuestra solución se complique al punto que haga difícil su entendimiento, pues esto atenta directamente contra la calidad y mantenibilidad del código.

Tengamos en cuenta que los términos complejo y complicado deben ser entendidos como:

  • Complejo: compuesto por muchas partes interconectadas

  • Complicado: tan complejo que dificulta su entendimiento

Plano es mejor que anidado

Hay quienes dicen que una porción de código con más de tres niveles de indentación, debe ser revisada y reescrita o refactorizada. La causa de esto es que más de tres niveles de indentación hacen que el código sea poco comprensible para otros o para nosotros mismos si volvemos a él varios meses después. Este tipo de código es propenso a errores y demanda de mucho tiempo de análisis para llegar a sintonizarnos con su lógica.

Espaciado es mejor que denso

Este principio tiene mucho que ver con la apariencia visual del código escrito en Python. Favorece el empleo de espacios en blanco para delimitar los bloques de código, separando grupos de sentencias con fines comunes, en lugar de presentarlo todo junto y apilado. Un espaciamiento lógico del código contribuye a la legibilidad del mismo a cambio de unos pocos bytes más de espacio en disco.

La legibilidad cuenta

Comencemos el análisis de este punto con una cita textual:

“…programs must be written for people to read, and only incidentally for machines to execute.”

Structure and Interpretation of Computer Programs

Lo que en español viene siendo:

“…los programas deben ser escritos para que las personas los lean, y solo incidentalmente para que las máquinas los ejecuten.”

Esta idea resulta fundamental para cualquier programador y cualquier lenguaje. Si tu código no es claro y legible, tus colegas y colaboradores deberán dedicar muchísimo tiempo para entenderlo y desentrañar su lógica, con el consiguiente impacto negativo en su productividad.

La mantenibilidad del código se verá seriamente afectada también, lo que puede ocasionar que intentos posteriores por mejorar su base de código fracasen rotundamente.

Este debe ser, por mucho, el principio más aclamado por los creadores y la Comunidad de Python como una necesidad y no como una opción.

La legibilidad tiene que ver, entre otras cosas, con:

  • La selección adecuada de nombres para variables, funciones, clases, módulos y paquetes

  • El espaciamiento y la puntuación coherentes del código

  • El empleo de parámetros keywords en funciones y métodos

  • El empleo consistente de un estilo único de codificación en todo el proyecto o sistema

  • El uso de comentarios y docstring donde resulte pertinente.

Los casos especiales no son tan especiales como para romper las reglas

Este principio tiene que ver con el hecho de que nuestro código debe ser lo suficientemente genérico como para funcionar correctamente en la mayoría de los casos, de forma tal que no sea necesario ir valorando caso por caso hasta cubrir todo el abanico de posibilidades, lo que puede resultar en una tarea interminable y sin garantías de éxito, pues siempre pueden apareces casos no previstos.

Aunque la práctica golpea a la pureza

Ahora podría parecer que nos decimos y nos contradecimos, el principio anterior nos compele a no dejarnos influenciar por las excepciones de la regla y este viene a permitírnoslo. Es cierto, en ocasiones una forma de hacer las cosas aceptada generalmente como “normal” puede interponerse en el camino del principio anterior. Si es así, pues ya tenemos permiso para violarlo en nombre de la práctica aceptada.

Se trata de una cuestión de balance o equilibrio. Por ejemplo: a veces debemos sacrificar un poco la legibilidad para ganar en desempeño y eficiencia, todo dependerá de nuestros objetivos y metas.

Los errores nunca deben pasar silenciosamente

Python cuenta con un potente sistema de manejo de excepciones, sin embargo, hay situaciones en las que pasamos por alto algunos errores o problemas que puede generar una porción de código en función del contexto de ejecución. También es común ver que se emplea la clase base Exception o peor, no se emplea excepción alguna en los bloques try...except. Esto probablemente se debe a que no tenemos idea del tipo o los tipos de excepciones que nuestro código puede generar y con esta práctica nos “aseguramos” de que el sistema no colapse cuando se produzca un error o excepción. Sin embargo, esto es como caminar sobre un campo minado, por tanto, es recomendable que nos detengamos a revisar nuestro código para asegurarnos de que estamos capturando las excepciones o errores que esperamos que ocurran y no otros. En pocas palabra, siempre emplea excepciones específicas en tus bloques try…except, esto te pone en control de la situación y te alerta si sucede algo no previsto.

Finalmente debemos hacer notar que no todas las excepciones constituyen errores. Por ejemplo, la excepción StopIteration no representa un error, sino que es parte del funcionamiento interno del lenguaje para el trabajo con iteradores y es lanzada para señalar que no hay más elementos en el iterador.

Como convención, las excepciones integradas (built-in) en el lenguaje que representan errores, concluyen su nombre no la palabra “Error”. Algunos ejemplos: TypeError, ValueError, KeyError… Esta es una convención muy fuerte, por lo que te recomendamos emplearla en tus propias excepciones.

A menos que hayan sido explícitamente silenciados

Aquí llegamos a la segunda parte de la historia. Este comportamiento denota que estamos en control de nuestro código, que sabemos a ciencia cierta los errores que pueden ocurrir y que podemos silenciarlos elegantemente según nuestras necesidades.

Ante la ambigüedad, rechaza la tentación de adivinar

Con frecuencia nos enfrentamos a situaciones donde ciertos aspectos del código o de los objetos que estamos manipulado no están claros del todo. Esto nos hace querer proceder usando el estándar más común o las solución generalmente aceptada como buena. Imaginemos, por ejemplo, que trabajamos con un código que procesa archivos de texto plano almacenados en nuestro disco duro y que no contamos con información sobre la codificación empleada. En sistemas Unix podemos pensar que UTF-8 vendría bien, en sistemas Windows sin embargo, el estándar podría ser cp1252. ¿Qué tal si asumiendo una codificación equivocada, corrompemos los datos contenidos en los archivos manipulados?

Debe haber una –y preferiblemente solo una– forma obvia de hacerlo

Este principio se aplica fundamentalmente al desarrollo de frameworks y librerías, en los cuales debemos diseñar una API (Application Programing Interface) concisa, donde el usuario disponga una única vía para ejecutar cada acción. Si proveemos varias maneras de proceder, estaremos creando incertidumbre, pues el usuario de nuestro código no estará seguro de si está empleando la opción correcta incluso si conoce todas las opciones disponibles. Además, nuestra API demandará más tiempo de aprendizaje y obligará al usuario a tener que recordar más cosas.

Si nos detenemos a pensar en este principio nos damos cuenta de que el propio Python lo viola recurrentemente, por ejemplo, existen varias vías para crear las estructuras de datos fundamentales del lenguaje. Veamos un ejemplo:

>>> my_dict = {'a': 1, 'b': 2}

>>> my_dict

{'b': 2, 'a': 1}

>>> my_dict = dict(a=1, b=2)

>>> my_dict

{'b': 2, 'a': 1}

>>> my_dict = {k: v for k, v in zip(['a', 'b'], [1, 2])}

>>> my_dict

{'b': 2, 'a': 1}

>>> my_dict = dict(zip(['a', 'b'], [1, 2]))

>>> my_dict

{'b': 2, 'a': 1}

Esto, lejos de ser un inconveniente se convierte en una ventaja, pues le aporta flexibilidad lenguaje. De cualquier manera estamos respaldados, en este caso, por el principio: “Aunque la práctica golpea a la pureza”

Aunque esa forma puede no ser obvia a la primera, a menos que seas holandés

Este, más que un principio, es un reconocimiento a Guido Van Rossum el creador del lenguaje. Sin embargo, resalta el hecho de que no todas las personas ven las cosas de la misma manera. Lo que para algunos puede parecer obvio, para otros puede resultar incompresible o ilógico.

Ahora es mejor que nunca

Como reza el proverbio: “No dejes para mañana lo que puedes hacer hoy”. Esto entraña una enseñanza importante para la vida y para la programación también. Desde el momento en que descubrimos que algo no funciona bien, debemos buscarle solución lo más pronto posible, de lo contrario lo iremos dejando a un lado y luego nos olvidaremos de ello y de la información necesaria para solucionarlo.

Aunque nunca a menudo es mejor que “ahora mismo”

Ok, tampoco hay que ser obsesivo con las cosas. A veces es necesario esperar a que se definan otras cuestiones para poder resolver lo que nos ocupa. Es importante para esto, considerar el entorno: las partes interesadas, usuarios, colaboradores, otros programadores del equipo, etc.

Si la implementación es difícil de explicar, es una mala idea

La lógica de este principio se explica por sí sola. De esta forma podemos identificar cuando hemos cruzado la frontera entre lo simple y lo complejo o entre lo complejo y lo complicado.

A veces el solo ejercicio tratar de explicarnos a nosotros mismo la implementación de algo en voz alta, nos hacer reconsiderar muchas cosas que antes nos parecían correctas y sencillas.

Si la implementación es fácil de explicar, puede que sea una buena idea

Puede pareces que este principio es igual que el anterior, pero a la inversa. Sin embargo, hay que notar que en esta ocasión cambia la acción de “es” a “puede que sea”, dejando claro que no estaremos seguro hasta que lo probemos en la práctica.

Los espacios de nombres son una magnífica gran idea – ¡hagamos más de esas!

Los espacios de nombres constituyen un elemento esencial en la estructura del lenguaje. Ellos nos evitan problemas tan serios como la colisión de nombre y nos facilitan la organización coherente de nuestro código.

Este principio es básicamente un llamado a realizar aportes relevantes al lenguaje para hacerlo cada vez mejor.

Y eso es todo…

Como podemos ver la lógica de estos principios es realmente acertada y refleja la sencillez del sentido común aplicado al desarrollo de un lenguaje de programación. En ellos se resume la esencia de la filosofía detrás del desarrollo de Python.

El Zen de Python ha sido formalizado en PEP 20 (Python Enhancement Proposals), donde el resumen reza:

“Long time Pythoneer Tim Peters succinctly channels the BDFL’s guiding principles for Python’s design into 20 aphorisms, only 19 of which have been written down.”

PEP-0020

Pythonista por mucho tiempo Tim Peters sucintamente canalizó los principios guías del BDFL para el diseño de Python en 20 aforismos, solo 19 de los cuales han sido escritos.

Conclusión

Aplicar consistentemente los principios contenidos en el Zen de Python puede ayudarnos a escribir código de alta calidad, legibilidad y mantenibilidad; lo que sin dudas, nos puede convertir en programadores de éxito.

Lecturas Recomendadas

Para profundizar un poco más en el contenido de estos principios y de la filosofía de Python en general, recomendamos la lectura del Capítulo I “Principles and Philosophy” del libro “Pro Python” de Marty Alchin publicado por la Editorial Apress.

Bien, esto es todo por ahora, si este artículo te resultó interesante y/o útil, compártelo para que otros también puedan acceder a él. Déjanos tus comentarios y podremos mejorar nuestros contenidos.

Gracias de antemano,

lpozo