Buenas Prácticas de Programación con Python

En toda disciplina del saber humano la experiencia práctica es fundamental. De la práctica se derivan lecciones muy importantes que difícilmente se olvidan. Estas lecciones generalmente se convierten en lo que se suele denominar buenas prácticas.
Te propongo un resumen de lo que podrías considerar buenas prácticas de programación con Python que te ayudará a organizarte mejor y te harán más fácil la experiencia de programar con Python.


Tabla de Contenidos


Buenas Prácticas de Programación con Python

Este resumen de buenas prácticas de programación con Python no pretender ser exhaustivo o creer se trata de la verdad absoluta, pues simplemente no hay nada absoluto, lo que hoy es considerado bueno, mañana puede no serlo, todo depende del contexto y hasta del individuo, además, Python es un ente vivo que evoluciona todos los días.

Organización General del Código

  • La estructura general de un módulo debería ser como se muestra a continuación:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figura 1. Estructura Recomendada para un Módulo/Script Python

  • Si tus módulos pueden ser importados y también ejecutados directamente como scripts, puedes valerte de un truco para evitar la ejecución innecesaria del código cuando importes el módulo.El truco consiste en incluir al final del módulo el código siguiente:
if __name__ == '__main__':
    main()  # main() debe ser la función principal del script
  • Agrupa en módulos el código con objetivos y funcionalidades similares o estrechamente relacionadas, luego impórtalos desde donde los necesites
  • Evita crear paquetes innecesarios siempre que puedas, créalos solo cuando necesites agrupar una gran cantidad de código/módulos. Los paquetes complejizan tu espacio de nombres (namespace) y tus operaciones import, también te obligan a teclear más, por ejemplo:
>>> from root.tools.video import converter  # Operación import con paqutes múltiples
>>> from video import converter  # Operación import directamente desde un módulo
  • Emplea nombres cortos y descriptivos para tus scripts, módulos y paquetes
  • Crea tu propio conjunto de módulos y scripts utilitarios, modifica tu PYTHONPATH y úsalos regularmente en tus proyectos, esto maximiza la reutilización del código y agiliza tu proceso de desarrollo
  • Emplea una estructura de paquetes y directorios coherente en tus proyectos, la Figura 2 podría servirte de guía, con la observación de que el paquete principal (mypackage) debería tomar el mismo nombre de tu proyecto o aplicación

 

 

 

 

 

Figura 2. Estructura Básica Recomendada para un Proyecto Python

Entorno de Trabajo y Herramienta

  • Consíguete un buen Editor de Texto o un buen IDE para Python, configúralo según tus necesidades y domínalo a la perfección para que seas más productivo. Por ahí abundan estas herramientas; recomendación: PyCharm IDE, Sublime Text, Visual Studio Code
  • Aprende a crear y a trabajar con entornos virtuales (virtual environments) para evitar convertir tu sistema base en un lío
  • Emplea un Sistema de Control de Versiones para trabajar tus proyectos de manera consistente y evitar pérdidas irreparables. Recomendación: Git
  • Si pretendes hacer que tu proyecto o aplicación sea Software Libre u Open Source y que otros desarrolladores colaboren, debes valorar la Gestión de Proyectos desde GitHub
  • Emplea linters para chequear y revisar automáticamente tu código. Por ejemplo: pylint, flake8, pychecker, entre otros
  • Automatiza la creación de la documentación de tus proyectos con herramientas como Sphinx

Estilo de Codificación (Coding Style)

  • Aprende a leer y escribir en Inglés y usa siempre el Inglés en tu código, en tus comentarios y en tus docstrings. Esto se torna mucho más necesario si pretendes que tu código tenga alcance internacional
  • Asume un estilo de codificación y mantenlo a lo largo de todo el proyecto. El PEP 8 es un excelente lugar por donde comenzar
  • Comenta el código siempre que lo creas necesario, pero ten presente que un código Python bien escrito y legible no requiere de muchos comentarios
  • Si actualizas tu código, actualiza también tus comentarios; no hay peor comentario que uno desactualizado
  • No comentes lo obvio:
>>> # Sumando x + y
>>> x + y
  • Emplea tus comentarios para describir el porqué en lugar del qué
  • Los comentarios pueden convertirse en trabajo doble para ti, por eso trata de que tu código sea lo más legible posible y así los evitas. Cuidado con los comentarios, a veces denotan la necesidad de refactorizar el código
  • No te repitas a ti mismo, aplica el principio DRY (Don’t Repeat Yourself). Cuando identifiques código repetitivo o similar o cuando te sorprendas copiando y pegando fragmentos de código, extrae el fragmento, conviértelo en una función o método y llámalo desde los lugares donde lo requieras
  • Usa coherentemente la indentación del código, esto mejora notablemente la legibilidad del mismo y en Python es parte de la sintaxis del lenguaje
  • Para la indentación es recomendable usar espacios (de preferencia 4) en lugar de caracteres de tabulación, debido a que diferentes sistemas/editores pueden tener diferentes configuraciones de tabulación
  • Nunca mezcles espacios con caracteres de tabulación en la indentación
  • Emplea nombres cortos y descriptivos para tus variables, funciones, clases, métodos, atributos, módulos y paquetes. Los nombres son clave para la legibilidad del código, selecciónalos adecuadamente
  • No emplees nombres para tus variables y atributos, que colisionen con las palabras reservadas o los nombres de estructuras propias del lenguaje (built-ins), si esto fuera estrictamente necesario para garantizar la legibilidad del código, añade un guión bajo al final del nombre para diferenciarlo, ejemplo: list_ = [1, 2, 3, 4]
  • Evita usar la sentencia from module import *, conocida como Wildcard imports, que además de atiborrar tu espacio de nombres con atributos que no vas a emplear, resulta contraproducente en términos de legibilidad de código. Recuerda, “la legibilidad cuenta”
  • Evita las abreviaturas
  • Mantén el código simple, aplica el principio KISS (Keep It Simple Stupid). Recuerda siempre que “Simple es mejor que complejo”
  • No emplees el operador == para comparar objetos de tipo singleton como None, en su lugar emplea el operador is o is not, como en:
def my_func(lst=None):
    if lst is None:
        lst = []
    # Process lst...

Pruebas al Código (Testing)

  • Desarrolla una batería consistente y abarcadora de pruebas para tu código. Crea casos de prueba con valores típicos o esperados y con valores en la frontera o límite, incluso con valores absurdos
  • Automatiza tus pruebas de código, empleando herramientas como nose y pytest
  • Escribe los casos de prueba antes que el propio código, esto te ayudará a definir mejor el problema y los requisitos de la solución y de seguro incrementará la calidad de tu código
  • Emplea el ciclo Red/Green/Refactor para tus pruebas, es decir, Red: escribe tu prueba y córrela (Red = no pasa), Green: escribe el código necesario para que tu prueba pase (Green = pasa), Refactor: modifica tu código para que quede mejor escrito, sea más legible y más eficiente, pero manteniendo sus resultados finales. Corre nuevamente las pruebas
  • Realiza pruebas de lo que estás codificando a cada momento. Asegúrate de que funciona correctamente antes de incluirlo en tu código final, para esto te puedes auxiliar del una Sección Interactiva del Intérprete Python, o de otras herramientas como IPython
  • Realiza pruebas al código que se relacione con el código que estás modificando (no solo al código modificado). Asegurarte de que toda tu base de código continúa funcionando correctamente y evita las regresiones
  • Cada prueba debe ser independiente; en otra palabras, los resultados de una prueba no deben depender de los resultados de otra(s)

Manejo de Errores y Excepciones

  • Lanza excepciones a bajo nivel y atrápalas a alto nivel, así podrás tener un código más fácil de mantener, pues las modificaciones/actualizaciones serán en solo unos pocos lugares
  • Como regla general, es mejor ser específico que genérico a la hora de manejar las excepciones, por ejemplo: evita usar la clase Exception en un bloque try/except. Exception es una clase de excepción muy amplia, lo que significa que con ella atraparás lo que quiere atrapar y lo que no. Recuerda que los “errores no deben pasar silenciosamente”
  • Usa siempre excepciones basadas en clases, para garantizar compatibilidad con versiones más recientes de Python
  • Implementa tus propias excepciones solo cuando creas que las disponibles en Python no satisfacen tus necesidades específicas

Seguridad

  • Valida siempre los datos de entrada suministrados por el usuario, antes de realizar cualquier operación con ellos. Recuerda que el usuario es capás de todo
  • Nunca manejes/transmitas datos sensibles del usuario en forma de texto plano u otra forma no segura. Emplea herramientas de encriptación siempre que lo requieras
  • Cuando emplees código de terceros, asegúrate de que ha sido ampliamente probado y que funciona correctamente, y que además, mantiene un desarrollo activo. Siempre que puedas, léete ese código que, aunque es de terceros, va a formar parte de tu producto final
  • Mantén todo el código de terceros actualizado siempre que sea posible
  • Como de seguro no tendrás el tiempo y/o la habilidad de escribir código perfecto, concéntrate en los fragmentos de tu código que se encargan de recibir y procesar información del exterior, no solo del usuario, sino también de archivos, de sitios web, de otras aplicaciones, etc.
  • Si pretendes incluir extensiones escritas en C/C++ en tus proyectos, sé cuidadoso con el manejo de memoria y otras vulnerabilidades clásicas de estos lenguajes
  • Evita por todos los medios, que tus aplicaciones requieran correr con privilegios de administración (root) y si es imprescindible hacerlo, minimiza el tiempo de empleo de estos privilegios. Recuerda que esto puede comprometer la seguridad del sistema completo y puede resultar extremadamente peligroso y costoso

Programación Orientada a Objetos

  • Aprende y emplea oportunamente la Programación Orientada a Objetos (OOP por sus siglas en Inglés), esto mejorará la mantenibilidad y la extensibilidad de tu código, y te ahorrará mucho tiempo de desarrollo y de depuración de errores (debugging)
  • Evita la herencia múltiple (multiple inheritance), puede no funcionar como esperas
  • Emplea la técnica del Duck Typing para evitar el uso de la herencia cuando sea posible
  • Favorece la Composición en lugar de la Herencia, la primera es mucho más flexible y controlable
  • Favorece el Polimorfismo en lugar del chequeo de tipos (type checking)
  • Aplica el principio Open/Close, procurando que tu código quede abierto a la extensión y cerrado a la modificación
  • El uso de la función integrada super() debe ser consistente, es decir, en una jerarquía de clases, super() debe usarse en todos o en ningún lugar. Mezclar super() y la llamada clásica es una práctica confusa
  • No mezcles old-style y new-style clases si trabajas con Python 2.x: tener un código base con ambos estilos de clases resulta en un comportamiento MRO (Method Resolution Order) variable
  • Aprende Patrones de Diseño orientado a objetos y empléalos para mejorar la mantenibilidad y el diseño de tu código
  • Emplea coherentemente las convenciones de nombrado:
    • self para identificar al objeto concreto en la definición de clases,
    • cls para identificar la clase en los métodos de clase (classmethod),
    • guión bajo al inicio del nombre de atributos privados y doble guión bajo para invocar el name mangling de Python y evitar la colisión de nombre en jerarquías de clases
  • No accedas a atributos o miembros de clases que hayan sido definidos como privados, desde fuera de la clase en cuestión

Optimización de Código

  • Regresa a tu código algún tiempo después y revísalo en busca de mejoras, posibles errores y oportunidades de optimización, pero ojo, no te obsesiones con la optimización, este afán puede romper tu código y hacerte perder valiosas horas de tu tiempo en vano
  • Antes de pensar en optimizar tu código, asegúrate de que has logrado que funcione correctamente, esto es lo más importante. Luego asegúrate de tener una buena batería de pruebas (tests) y solo entonces puedes pensar en optimizar
  • Si ya tienes todos los elementos del punto anterior y has decidido firmemente optimizar, primero debes caracterizar (profile) tu código. Hay varias herramientas disponibles para esto, por ejemplo: cProfile, pstats, o RunSnakeRun y SnakeViz si prefieres las interfaces gráficas. El code profiling es sumamente importante, pues te dirá exactamente qué porción de tu código requiere optimización. Un sencillo ejemplo de cómo hacer code profiling con la secuencia de Fibonacci:

file: fibonacci.py

import cProfile


def fib(n):
    a, b = 0, 1
    for i in range(0, n):
        a, b = b, a + b

    return a


def fib_seq(n):
    return [fib(i) for i in range(0, n + 1)]

cProfile.run('print(fib_seq(10)), print')
$ python3 fibonacci.py
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
         17 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 fibonacci.py:12(fib_seq)
        1    0.000    0.000    0.000    0.000 fibonacci.py:13(<listcomp>)
       11    0.000    0.000    0.000    0.000 fibonacci.py:4(fib)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
  • Considera usar generadores (generator expression) e iteradores, siempre que estés trabajando con una función que devuelva una secuencia o que trabajes en un ciclo for
  • Emplea algoritmos apropiados y óptimos para tu código, por ejemplo, si necesitas sumar números enteros desde 1 hasta n, puedes hacer lo siguiente:
>>> # Cuál de estos dos algoritmos será más eficiente?
>>>
>>> # Calcula la suma de enteros de 1 hasta n usando la iteración simple
>>> n = 100
>>> sum = 0
>>> for number in range(1, n + 1):  # Este ciclo se ejecuta 100 veces
...    sum += number
...
>>> print(sum)
5050
>>>
>>> # Calcula la suma de enteros de 1 hasta n usando el algoritmo apropiado
>>> print(n * (1 + n) / 2)
5050.0
  • A propósito de los algoritmos, no trates nunca de reinventar la rueda, seguramente ya alguien se enfrentó al problema que estás tratando de solucionar y ha llegado a una solución adecuada, así que, busca primero en la librería estándar y en librerías de terceros antes de aventurarte a resolver por tu cuenta
  • Emplea estructuras de datos optimizadas para las operaciones que deseas realizar. Por ejemplo, las tuple son estructuras muy eficientes en el uso de memoria, los set están optimizados para operaciones de pertenencia o membrecía (con el operador in), los deque del módulo collections de la librería estándar, están optimizados para operaciones append y pop en ambos extremos de la colección, y así sucesivamente
  • Para crear/inicializar estructuras de datos, favorece el empleo literales en lugar de las funciones: str(), list(), tuple(), dict(), etc.
>>> lst = [1, 2, 3, 4]  # Esto es más rápido que...
>>> lst = list((1, 2, 3, 4))  # Esto
  • Minimiza el número de llamadas a funciones en tu código, por ejemplo:
# Cuál de los dos ciclos es más rápido?

x = 'a very long string...'

i = 0
while i < len(x):  # len() se evalúa cada vez que se repite el ciclo
    print(x[i])
    i += 1

#######################################

i = 0
strlen = len(x)  # len() solo se evalúa una vez
while i < strlen:
    print(x[i])
    i += 1
  • Importa nombres específicos con la sentencia from module import attribute, esto incluye el nombre del atributo en tu espacio de nombres en tiempo de importación y mejora el desempeño en tiempo de ejecución

Conclusión

Las buenas prácticas de programación con Python que acabas de leer en este artículo, te servirán de guía a la hora de desarrollar tus programas y aplicaciones con Python.

Estas prácticas constituyen un resumen de los resultados, las experiencias y el quehacer de varios autores y desarrolladores de Python y sin dudas te serán de mucha utilidad en tu trabajo y/o en tu aprendizaje del lenguaje.

Gracias por la lectura,

lpozo

2 comentarios

    • yrh on 10 enero, 2018 at 9:05 pm

    Hola quiero iniciarme en la programación con Python y creo que este sitio es muy bueno. Ojalá lo mantengan actualizado y no muera como otros de la plataforma.

    1. Eres bienvenido. Nuestro objetivo es mantenerlo y que sirva a todos los que se inician o ya están usando Python.
      saludos,
      lpozo

Los comentarios han sido desactivados.