Buenas Prácticas en Python¶
Introducción a las Buenas Prácticas¶
Las buenas prácticas en programación son un conjunto de recomendaciones y convenciones que ayudan a los desarrolladores a escribir código que sea limpio, legible, mantenible y eficiente. Al seguir estas prácticas, no solo se mejora la calidad del código, sino que también se facilita su comprensión y modificación por otros programadores (o incluso por el propio autor en el futuro).
¿Por qué son importantes las Buenas Prácticas?¶
Legibilidad: El código legible es fácil de entender y seguir. Esto reduce el tiempo que otros desarrolladores (o tú mismo) necesitan para aprender cómo funciona el código, lo que facilita la colaboración y el mantenimiento.
Mantenibilidad: Un código bien estructurado y documentado es más fácil de modificar o extender sin introducir errores. Esto es crucial en proyectos grandes o cuando el código es utilizado por diferentes personas o equipos.
Eficiencia: Aplicar buenas prácticas puede llevar a código más eficiente, tanto en términos de velocidad de ejecución como de uso de recursos.
Reducción de Errores: Las buenas prácticas fomentan la prevención de errores comunes y facilitan su identificación y corrección temprana.
Profesionalismo: Seguir buenas prácticas demuestra un nivel de profesionalismo y dedicación a la calidad del software, lo que puede mejorar la reputación y credibilidad de un desarrollador o equipo.
Problemas Comunes al No Seguir Buenas Prácticas¶
Cuando no se siguen buenas prácticas, pueden surgir varios problemas:
Código difícil de entender: Sin una estructura clara y comentarios adecuados, el código puede ser confuso y difícil de entender, incluso para el autor original después de un tiempo.
Errores difíciles de identificar: La falta de manejo adecuado de errores y excepciones puede llevar a fallos en el código que son difíciles de rastrear y corregir.
Duplicación de código: Copiar y pegar código en lugar de reutilizar funciones puede llevar a redundancias y errores difíciles de mantener.
Baja eficiencia: El código mal optimizado puede resultar en un uso excesivo de memoria o tiempo de procesamiento, afectando el rendimiento del programa.
Difícil mantenimiento y escalabilidad: Un código mal estructurado es difícil de modificar o escalar, lo que puede ser costoso en términos de tiempo y recursos.
Objetivos de las Buenas Prácticas¶
Los objetivos principales de las buenas prácticas en programación son:
Fomentar un estilo de codificación coherente: Seguir un conjunto de reglas y convenciones ayuda a que el código de diferentes desarrolladores tenga una apariencia uniforme.
Facilitar la revisión y el trabajo en equipo: Un código bien escrito y documentado permite que otros desarrolladores revisen, entiendan y colaboren más fácilmente.
Mejorar la calidad del software: Aumentar la calidad general del código, haciendo que sea más robusto, menos propenso a errores, y más fácil de mejorar.
Aumentar la eficiencia del desarrollo: Las buenas prácticas ayudan a los desarrolladores a trabajar más rápido y con menos errores, reduciendo el tiempo necesario para escribir, revisar y depurar el código.
Ejemplos de Buenas Prácticas¶
Algunos ejemplos de buenas prácticas que veremos en detalle durante esta clase incluyen:
Adherirse a las convenciones de estilo de código, como las definidas por PEP 8.
Documentar adecuadamente el código mediante docstrings y comentarios.
Utilizar nombres descriptivos para variables, funciones y clases.
Escribir funciones y métodos cortos que realicen una única tarea.
Manejar errores y excepciones de manera eficaz para prevenir fallos inesperados.
Optimizar el código para mejorar su rendimiento.
Implementar pruebas automáticas para asegurar la calidad del código.
Esta introducción proporciona el contexto y la motivación detrás de cada una de las prácticas que discutiremos en profundidad durante la clase.
Uso de PEP 8 - Estilo de Código¶
El PEP 8 (Python Enhancement Proposal 8) es una guía de estilo oficial para escribir código Python que establece convenciones para asegurar que el código sea legible y consistente. Seguir estas convenciones ayuda a que los desarrolladores trabajen de manera colaborativa sin fricciones debido a diferencias de estilo. A continuación, se presentan los aspectos más importantes del PEP 8:
Identación¶
Uso de 4 espacios por nivel de indentación: El PEP 8 recomienda utilizar 4 espacios para cada nivel de indentación. El uso de espacios en lugar de tabulaciones asegura que el código se vea igual en diferentes entornos de desarrollo.
def mi_funcion(): if True: print("Indentado con 4 espacios")
Consistencia en la indentación: No mezclar espacios y tabulaciones en un mismo archivo.
Longitud de Línea¶
Longitud máxima de línea de 79 caracteres: Las líneas de código no deben exceder los 79 caracteres para asegurar la legibilidad en diferentes dispositivos y ventanas de edición. Para evitar esto:
Utiliza paréntesis para romper líneas largas de código. Los paréntesis permiten que las expresiones largas se extiendan a múltiples líneas sin necesidad de una barra invertida.
Utiliza la barra invertida (
\) para separar líneas cuando no sea posible usar paréntesis o si prefieres una sintaxis explícita.
Ejemplo de uso de paréntesis para separar líneas:
resultado = ( funcion_larga(parametro1, parametro2) + otra_funcion(parametro3, parametro4) - tercer_funcion(parametro5) )
Ejemplo equivalente utilizando barras invertidas para separar líneas:
resultado = funcion_larga(parametro1, parametro2) + \ otra_funcion(parametro3, parametro4) - \ tercer_funcion(parametro5)
Ejemplo de concatenación automática de cadenas de texto usando paréntesis:
mi_variable = ( "Este es un texto muy largo que no cabe en una sola línea, " "pero al colocar las cadenas en múltiples líneas dentro de " "paréntesis, Python las concatena automáticamente." )
Ejemplo de separación de una cadena de texto larga utilizando la barra invertida:
mi_variable = "Este es un texto muy largo que no cabe en una sola línea, " \ "por lo que se utiliza una barra invertida para continuar en " \ "la siguiente línea."
Espacios en Blanco¶
Uso adecuado de espacios alrededor de operadores y después de comas: - Coloca un espacio antes y después de los operadores de asignación (
=,+=,-=) y operadores aritméticos (+,-,*,/). - No uses espacios en blanco alrededor de paréntesis, corchetes o llaves.suma = 1 + 2 lista = [1, 2, 3]
Espacios en definiciones y llamadas de funciones o métodos: - No uses espacios en blanco inmediatamente dentro de los paréntesis de una llamada a función o método, o al definir parámetros de una función. - Utiliza un espacio después de cada coma en la lista de parámetros de una función o al pasar argumentos.
Ejemplos correctos de uso de espacios en blanco:
# Definición de función def mi_funcion(parametro1, parametro2=None, parametro3=5): pass # Llamada a la función resultado = mi_funcion(10, parametro2=20)
Ejemplos incorrectos de uso de espacios en blanco:
# No usar espacios en blanco alrededor de los paréntesis def mi_funcion( parametro1 , parametro2 = None , parametro3 = 5 ): pass # No usar espacios en blanco alrededor de los argumentos resultado = mi_funcion( 10 , parametro2 = 20 )
Nombres de Variables, Funciones, Métodos y Clases¶
Nombres de variables, funciones y métodos en ``snake_case``: Los nombres de las variables, funciones y métodos deben estar en minúsculas, usando guiones bajos para separar palabras. Esto mejora la legibilidad del código y sigue la convención estándar de Python.
def calcular_total(): mi_variable = 10 class Calculadora: def sumar(self, numero1, numero2): return numero1 + numero2
Evita nombres de una sola letra, excepto en variables temporales o contadores de bucles (i, j, k).
Usa nombres descriptivos que indiquen claramente la función o propósito de la variable, función o método.
Evita el uso de palabras reservadas de Python (como class, def, lambda) para nombrar variables, funciones o métodos.
Nombres de clases en ``CamelCase``: Las clases deben nombrarse usando la convención CamelCase, donde cada palabra comienza con una letra mayúscula. Esta convención diferencia fácilmente las clases de las funciones, variables y métodos.
class MiClaseEjemplo: pass
Utiliza nombres que describan claramente la responsabilidad o función de la clase.
No uses guiones bajos ni caracteres especiales en los nombres de clases.
Las clases que heredan de otras clases base deben tener nombres que indiquen claramente su propósito o diferenciación respecto a la clase base.
Nombres de constantes en ``UPPER_CASE``: Las constantes deben ser nombradas usando letras mayúsculas, con guiones bajos para separar palabras. Esto indica que el valor de la constante no debe ser modificado.
MAX_VALOR = 100 PI = 3.14159
Prefijos Especiales para Variables y Métodos: - Usa un guion bajo simple (_) antes del nombre de una variable o método para indicar que es «protegido». Esto sugiere que el atributo o método no debe ser accedido desde fuera de la clase o módulo donde fue definido, aunque sigue siendo posible hacerlo. Esta es una convención de uso, no una restricción absoluta.
class Ejemplo: def _metodo_protegido(self): pass
Usa un doble guion bajo (__) antes del nombre de una variable o método para indicar que es «privado». Esto activa una transformación de nombre (name mangling) que cambia el nombre del atributo para incluir el nombre de la clase, dificultando su acceso desde fuera de la clase. Es útil para evitar conflictos de nombres en las subclases.
class Ejemplo: def __metodo_privado(self): pass
Nombres de módulos en ``snake_case``: Los nombres de los módulos (archivos .py) también deben seguir la convención snake_case. Esto facilita la legibilidad y el mantenimiento del código, especialmente cuando los módulos son importados en otros archivos.
Ejemplo de nombres de módulos:
mi_modulo.py
calculadora_avanzada.py
Seguir estas convenciones de nombres mejora la legibilidad del código y facilita su mantenimiento, asegurando que otros desarrolladores puedan entender el propósito y uso de cada variable, función, método, clase y módulo rápidamente.
Organización de Importaciones¶
Importaciones en módulos separados: Las importaciones deben realizarse al inicio del archivo de código, cada una en su propia línea, para mejorar la claridad y la legibilidad del código. Evita usar importaciones relativas, ya que pueden ser confusas y dificultar la comprensión del código. En su lugar, utiliza importaciones absolutas que indiquen claramente la ubicación del módulo o paquete.
Organiza las importaciones en el siguiente orden:
Importaciones de módulos estándar de Python: Módulos integrados que vienen con la distribución de Python (por ejemplo, os, sys, math).
Importaciones de módulos de terceros: Módulos o paquetes instalados a través de gestores de paquetes como pip (por ejemplo, numpy, pandas).
Importaciones de módulos locales: Módulos o scripts desarrollados localmente o que forman parte del mismo paquete o proyecto (por ejemplo, from mi_modulo import mi_funcion).
Ejemplo de organización de importaciones:
import os import sys import numpy as np import pandas as pd from mi_modulo import mi_funcion
Importaciones específicas vs. importaciones de módulo completo: Cuando importes módulos completos, como import os, estás importando todo el módulo, lo cual es útil cuando necesitas múltiples funciones de un módulo. Sin embargo, cuando solo necesitas funciones específicas, como from os import path, es mejor hacer una importación específica para reducir el espacio de nombres y mejorar la legibilidad.
Ejemplo de importación específica:
from os import path from collections import defaultdict
Importación de múltiples elementos de un mismo módulo: Cuando necesites importar varias funciones o clases de un mismo módulo, puedes enumerarlas después de from module import, separadas por comas. Esto es más claro y eficiente que importar todo el módulo si solo necesitas algunos elementos específicos.
Ejemplo de importación de múltiples elementos:
from math import sin, cos, tan
Evita las importaciones con `import *`: Importar todo el contenido de un módulo usando from module import * es considerado una mala práctica, ya que puede causar conflictos de nombres y hacer que el código sea más difícil de entender y depurar. Siempre importa solo los nombres que realmente necesitas.
Agrupación de importaciones: Separa cada grupo de importaciones con una línea en blanco para mejorar la claridad visual y hacer evidente la distinción entre módulos estándar, módulos de terceros, y módulos locales.
Ejemplo de agrupación de importaciones:
# Importaciones de módulos estándar import os import sys from math import sin, cos, tan # Importaciones de módulos de terceros import numpy as np import pandas as pd # Importaciones de módulos locales from mi_modulo import mi_funcion
Usar alias significativos para importaciones: Cuando utilices alias para módulos importados (por ejemplo, import numpy as np), asegúrate de que el alias sea ampliamente reconocido o tenga un significado claro para evitar confusión. Usar alias puede ayudar a reducir la longitud de la línea y mejorar la legibilidad del código.
Ejemplo de alias significativos:
import numpy as np # Alias comúnmente utilizado import pandas as pd # Alias comúnmente utilizado
Orden alfabético dentro de cada grupo: Es una buena práctica ordenar las importaciones alfabéticamente dentro de cada grupo para facilitar su búsqueda y reducir la posibilidad de duplicados.
Ejemplo de orden alfabético dentro de cada grupo:
# Importaciones de módulos estándar import os import sys from math import cos, sin, tan # Importaciones de módulos de terceros import numpy as np import pandas as pd # Importaciones de módulos locales from mi_modulo import mi_funcion
Seguir estas prácticas para la organización de importaciones mejora la legibilidad y el mantenimiento del código, y facilita la colaboración en proyectos con múltiples desarrolladores.
Uso de Docstrings¶
Documentar funciones, clases y módulos: Utiliza docstrings para describir qué hace una función, clase o módulo. Los docstrings deben estar en la primera línea dentro de la definición de la función, clase o módulo. Un buen docstring debe ser claro, conciso y proporcionar suficiente información para que otros desarrolladores comprendan el propósito y uso del código.
Ejemplo de docstring para una función:
def funcion_ejemplo(parametro): """ Realiza un ejemplo sencillo. Esta función toma un parámetro y lo retorna sin modificaciones. Es útil para demostrar el formato de un docstring básico y cómo describir las funcionalidades de una función en Python. :param parametro: Descripción del parámetro. :return: Descripción del retorno. """ return parametro
Docstrings para funciones y métodos:
Indica claramente el propósito de la función o método.
Describe los parámetros de entrada, su tipo esperado y lo que representan.
Indica el tipo de retorno y una descripción de lo que se devuelve.
Usa el formato estándar de Python, como el estilo de Google, NumPy o Sphinx, para mantener la coherencia en el proyecto.
Ejemplo de docstring de función con múltiples parámetros:
def calcular_area_rectangulo(base, altura): """ Calcula el área de un rectángulo. Este método recibe la longitud de la base y la altura de un rectángulo y devuelve su área calculada como base * altura. Es útil para cálculos geométricos simples. :param base: La longitud de la base del rectángulo (float). :param altura: La longitud de la altura del rectángulo (float). :return: El área del rectángulo (float). """ return base * altura
Docstrings para clases:
Describe el propósito de la clase y cualquier información relevante sobre su uso.
Documenta el comportamiento general de la clase y su relación con otras clases.
Incluye una breve descripción de los atributos y métodos principales.
Ejemplo de docstring para una clase:
class Vehiculo: """ Clase que representa un vehículo. La clase Vehiculo almacena información básica sobre un vehículo, como su marca, modelo y año de fabricación. Proporciona métodos para simular acciones comunes del vehículo, como arrancar y detener. Atributos: marca (str): La marca del vehículo. modelo (str): El modelo del vehículo. anio (int): El año de fabricación del vehículo. Métodos: arrancar(): Imprime un mensaje indicando que el vehículo ha arrancado. detener(): Imprime un mensaje indicando que el vehículo se ha detenido. """ def __init__(self, marca, modelo, anio): self.marca = marca self.modelo = modelo self.anio = anio def arrancar(self): """Imprime un mensaje indicando que el vehículo ha arrancado.""" print(f"El {self.marca} {self.modelo} ha arrancado.") def detener(self): """Imprime un mensaje indicando que el vehículo se ha detenido.""" print(f"El {self.marca} {self.modelo} se ha detenido.")
Docstrings para módulos:
Proporciona una visión general del módulo, explicando su propósito y su funcionalidad general.
Describe las principales funciones, clases, excepciones y cualquier otra información relevante para los usuarios del módulo.
Ejemplo de docstring para un módulo:
""" Módulo de operaciones matemáticas. Este módulo contiene funciones para realizar diversas operaciones matemáticas, como el cálculo de áreas y perímetros de diferentes figuras geométricas. También incluye herramientas para validar entradas y manejar excepciones. Funciones: calcular_area_rectangulo(base, altura): Calcula el área de un rectángulo. calcular_perimetro_circulo(radio): Calcula el perímetro de un círculo. Excepciones: ValueError: Se lanza si los parámetros proporcionados no son válidos. """
Formatos de Documentación: Google, NumPy y Sphinx¶
Existen diferentes formatos para escribir docstrings en Python, cada uno con sus propias ventajas y desventajas. Los tres estilos más comunes son el de Google, NumPy y Sphinx.
Estilo de Documentación de Google
El estilo de documentación de Google es conocido por su simplicidad y legibilidad. Utiliza secciones claras y bien definidas para describir los parámetros, el tipo de retorno, y las excepciones que puede generar una función o método.
Ejemplo de docstring en estilo Google:
def calcular_area_rectangulo(base, altura):
"""
Calcula el área de un rectángulo.
Args:
base (float): La longitud de la base del rectángulo.
altura (float): La longitud de la altura del rectángulo.
Returns:
float: El área del rectángulo.
Raises:
ValueError: Si base o altura son negativos.
"""
if base < 0 or altura < 0:
raise ValueError("La base y la altura deben ser no negativas.")
return base * altura
Ventajas
Claridad y Legibilidad: Las secciones claramente definidas hacen que el docstring sea fácil de leer y entender.
Compatibilidad: Amplio soporte en herramientas de documentación y bibliotecas de análisis de código.
Simplicidad: Es fácil de adoptar, incluso para desarrolladores que no tienen experiencia previa con formatos de documentación.
Desventajas
Menos Detallado: En comparación con otros estilos, puede no ser tan detallado en términos de tipos o estructuras de datos complejas.
Convenciones Específicas: Las herramientas de análisis de código o generación de documentación pueden requerir configuraciones específicas para interpretar correctamente este formato.
Estilo de Documentación de NumPy
El estilo de documentación de NumPy es utilizado ampliamente en la comunidad científica y para proyectos de código abierto que requieren una descripción detallada y estructurada de funciones, parámetros y retornos. Este estilo es especialmente útil para describir funciones matemáticas, científicas y de procesamiento de datos.
Ejemplo de docstring en estilo NumPy:
def calcular_area_rectangulo(base, altura):
"""
Calcula el área de un rectángulo.
Parameters
----------
base : float
La longitud de la base del rectángulo.
altura : float
La longitud de la altura del rectángulo.
Returns
-------
float
El área del rectángulo.
Raises
------
ValueError
Si base o altura son negativos.
"""
if base < 0 or altura < 0:
raise ValueError("La base y la altura deben ser no negativas.")
return base * altura
Ventajas
Estructura Clara y Detallada: La división en secciones específicas permite una documentación muy detallada.
Ampliamente Adoptado: Popular en la comunidad científica, especialmente en bibliotecas de Python relacionadas con el análisis de datos, como NumPy, SciPy y pandas.
Compatible con Herramientas de Documentación: Muchas herramientas de documentación de Python, como Sphinx, soportan directamente este estilo.
Desventajas
Verborrea: Puede ser más extenso y detallado, lo que podría ser excesivo para funciones simples.
Curva de Aprendizaje: La adopción de este estilo puede requerir una curva de aprendizaje para los desarrolladores que no están familiarizados con él.
Estilo de Documentación de Sphinx
El formato de Sphinx es otro estilo popular de docstrings que se utiliza principalmente cuando se genera documentación automática utilizando la herramienta Sphinx. Este estilo utiliza directivas específicas (:param:, :type:, :return:, etc.) para estructurar la información de manera clara y detallada.
Ventajas: - Compatible con Sphinx, una herramienta potente para generar documentación HTML y PDF a partir del código fuente. - Utiliza directivas específicas que permiten una mayor personalización y control sobre el formato de la documentación. - Proporciona un equilibrio entre concisión y detalle, adecuado para una amplia gama de proyectos.
Desventajas: - Puede ser más complejo de aprender para principiantes debido a la sintaxis de las directivas. - Requiere un conocimiento básico de Sphinx para aprovechar todas sus funcionalidades.
Ejemplo de docstring en estilo Sphinx:
def calcular_area_triangulo(base, altura):
"""
Calcula el área de un triángulo.
Este método utiliza la fórmula matemática del área (0.5 * base * altura)
para calcular el área del triángulo.
:param base: La longitud de la base del triángulo.
:type base: float
:param altura: La longitud de la altura del triángulo.
:type altura: float
:return: El área del triángulo.
:rtype: float
:raises ValueError: Si alguno de los parámetros es negativo.
"""
if base < 0 or altura < 0:
raise ValueError("La base y la altura deben ser números positivos.")
return 0.5 * base * altura
Voz en las descripciones de los docstrings¶
Descripciones cortas:
Voz activa e imperativa: Las descripciones cortas deben ser directas y concisas, utilizando la forma imperativa para describir lo que hace la función, clase o módulo.
Ejemplos: «Calcula el área de un círculo.», «Devuelve la longitud de una lista.»
Descripciones largas:
Voz activa o pasiva: Las descripciones largas pueden expandir el propósito de la función, clase o módulo, proporcionando más contexto y detalle sobre cómo funciona o cómo se debe usar. Pueden ser escritas en voz activa para mantener claridad y precisión, o en voz pasiva si el sujeto no es tan relevante.
Ejemplos en voz activa: «Este método toma el radio de un círculo y utiliza la fórmula matemática del área (π * radio^2) para calcular y devolver el resultado.»
Ejemplos en voz pasiva: «El área del círculo es calculada usando la fórmula matemática (π * radio^2) basada en el radio proporcionado.»
Buenas prácticas al usar docstrings¶
Mantén la consistencia: Utiliza el mismo estilo de docstrings en todo el proyecto.
Sé claro y conciso: Proporciona suficiente detalle para que los desarrolladores comprendan el propósito y uso del código, sin ser redundante.
Actualiza los docstrings: Mantén los docstrings actualizados cuando se realicen cambios en el código.
Elige el formato adecuado: Usa Google para proyectos más simples, NumPy para proyectos más complejos o científicos, y Sphinx si se utilizará Sphinx para generar documentación.
Utilizar el estilo adecuado de docstrings y seguir buenas prácticas de documentación mejora la comprensión y mantenibilidad del código, facilita la colaboración y permite que herramientas automáticas generen documentación útil y clara.
Espaciado Vertical¶
Separar secciones de código con líneas en blanco: Usa líneas en blanco para separar funciones, clases, y bloques de código dentro de una función para mejorar la legibilidad. El espaciado adecuado facilita la lectura y comprensión del código al proporcionar una estructura visual clara.
Entre definiciones de funciones y clases: Deja dos líneas en blanco entre las definiciones de funciones a nivel de módulo y entre la definición de una clase y la siguiente función o clase. Esto ayuda a diferenciar claramente las secciones del código.
def primera_funcion(): pass def segunda_funcion(): pass class MiClase: pass
Dentro de una función o método: Utiliza una línea en blanco para separar bloques de código lógicos dentro de una función o método. Esto es especialmente útil cuando hay diferentes pasos o secciones lógicas, como validaciones, cálculos o retornos de datos.
def calcular_suma(valores): # Validar los valores de entrada if not valores: return 0 # Realizar la suma total = sum(valores) # Retornar el resultado return total
Después de los docstrings: Deja una línea en blanco después del docstring de una función, método o clase para separar la documentación del código.
def funcion_con_docstring(): """ Esta función realiza una tarea específica. Detalles adicionales sobre la función. """ # Inicia el código de la función print("Ejecutando función...")
Entre importaciones y el código: Deja una línea en blanco después de todas las importaciones para separar claramente las dependencias del código principal.
import os import sys def mi_funcion(): pass
Buenas Prácticas de Espaciado Vertical
Mantén la consistencia: Utiliza las mismas reglas de espaciado en todo el proyecto para mantener un estilo de código coherente.
Evita el exceso de líneas en blanco: Aunque es importante usar líneas en blanco para mejorar la legibilidad, evita usarlas en exceso, ya que pueden causar una apariencia desordenada o fragmentada del código.
Usa líneas en blanco estratégicamente: Coloca líneas en blanco donde ayuden a resaltar la estructura lógica del código, como entre bloques de control (if, for, while), declaraciones de variables importantes, o comentarios explicativos.
Ejemplos adicionales de uso de espaciado vertical
Separación entre bloques de control y lógica:
def procesar_datos(datos): # Validar datos de entrada if not datos: return "No hay datos para procesar." # Realizar algún procesamiento datos_procesados = [dato.upper() for dato in datos] # Devolver resultado return datos_procesados
Espaciado entre importaciones estándar, de terceros y locales:
# Importaciones de módulos estándar import os import sys # Importaciones de módulos de terceros import numpy as np import pandas as pd # Importaciones de módulos locales from mi_modulo import mi_funcion
Aplicar estas prácticas de espaciado vertical ayuda a estructurar el código de manera clara y organizada, facilitando su lectura, comprensión y mantenimiento.
Comentarios¶
Los comentarios son una herramienta esencial para hacer que el código sea más comprensible y fácil de mantener. Utilizados correctamente, los comentarios explican las intenciones y decisiones detrás del código, facilitando que otros desarrolladores (o tú mismo en el futuro) entiendan rápidamente lo que hace el código y por qué se tomó una determinada decisión de implementación. Esta sección proporciona recomendaciones sobre cómo escribir comentarios claros y efectivos que realmente añadan valor al código.
Comentarios claros y concisos: Los comentarios deben ser útiles y explicativos, no redundantes. Usa comentarios para explicar el «por qué» detrás de una decisión de código, no solo el «qué» hace el código. Los comentarios deben ayudar a otros desarrolladores (o a tu yo futuro) a entender la lógica, las decisiones de diseño y las partes complicadas del código.
# Utilizamos un bucle para calcular la suma de los números de la lista # en lugar de la función sum() porque la lista puede contener valores # personalizados que requieren un procesamiento adicional antes de la suma. for numero in lista: total += numero if numero > 0 else 0
Tipos de Comentarios
Comentarios en línea: Úsalos para explicar partes específicas de una línea de código. Colócalos después del código relevante, separados por al menos dos espacios y precedidos por un símbolo de almohadilla (#). Los comentarios en línea deben ser breves y solo se deben usar cuando la explicación es muy específica o el código es complejo.
resultado = calcular_total() # Llama a la función para obtener el total
Comentarios de bloque: Úsalos para explicar bloques de código más largos o complicados. Estos comentarios deben estar alineados con el bloque de código que describen y pueden estar compuestos de una o más líneas. Es ideal usarlos para describir la lógica general o el propósito de un conjunto de instrucciones.
# Verifica si todos los elementos de la lista son positivos. # Si algún elemento es negativo, el proceso se detiene. for elemento in lista: if elemento < 0: raise ValueError("Los elementos deben ser positivos.")
Docstrings: Aunque técnicamente no son comentarios, los docstrings son cadenas de texto que se usan al inicio de funciones, métodos, clases y módulos para documentar su propósito y uso. Utiliza docstrings para describir la funcionalidad general, mientras que los comentarios explican detalles específicos o decisiones de implementación dentro de esa funcionalidad.
Buenas Prácticas al Usar Comentarios
Explica el «por qué» y no solo el «qué»: Es más útil explicar por qué una sección de código está presente o por qué se ha implementado de una determinada manera, en lugar de simplemente describir lo que hace el código. Supón que el lector del comentario entiende Python, pero no necesariamente la lógica específica de tu código.
# Multiplicamos por 2 debido a los requisitos de la API externa resultado = valor * 2
Mantén los comentarios actualizados: Si modificas el código, asegúrate de que los comentarios reflejen los cambios realizados. Los comentarios obsoletos pueden ser más confusos que no tener comentarios en absoluto.
Evita comentarios redundantes: No comentes cosas que son obvias a partir del código en sí. Comentarios como «incrementa i en 1» junto a i += 1 no agregan valor y solo aumentan el ruido en el código.
i += 1 # Mal comentario: "incrementa i en 1" # Contador que se incrementa en cada iteración para seguimiento i += 1 # Comentario útil
Usa un estilo consistente de comentarios: Mantén un estilo de comentarios consistente a lo largo del proyecto. Por ejemplo, si comienzas cada comentario con una letra mayúscula y terminas con un punto, sigue ese formato en todo el código.
Usa comentarios para marcar partes del código en desarrollo o por mejorar: Utiliza marcas como TODO, FIXME o NOTE para identificar rápidamente las áreas que requieren atención o cambios futuros.
Nota
Las marcas deben estar en inglés, ya que muchas herramientas de desarrollo, como editores de texto y sistemas de control de versiones, están configuradas para reconocer y utilizar estas palabras clave en inglés.
# TODO: Optimizar esta sección para mejorar el rendimiento datos = procesar_datos() # FIXME: Solucionar el error cuando la lista está vacía if not lista: return None
Ejemplos Adicionales de Comentarios
Comentario para código complejo o de difícil comprensión:
# Utilizamos el algoritmo de Dijkstra porque permite encontrar el camino más corto # de forma eficiente en grafos con pesos no negativos. ruta_corta = encontrar_camino_minimo(grafo, nodo_inicial)
Comentario para explicaciones matemáticas o lógicas complejas:
# Calcula el coeficiente de correlación de Pearson. # Formula: r = Σ[(X_i - X_mean) * (Y_i - Y_mean)] / (n * σ_X * σ_Y) coef_pearson = calcular_correlacion_pearson(datos_x, datos_y)
Comentarios y Legibilidad del Código
Usa comentarios para separar secciones lógicas del código: Los comentarios también pueden servir para separar visualmente las diferentes secciones de un código largo, mejorando su legibilidad.
# Inicialización de variables x = 0 y = 1 # Cálculo principal resultado = x + y # Imprimir el resultado final print(resultado)
Al seguir estas prácticas, los comentarios se vuelven una herramienta poderosa para mejorar la claridad y comprensión del código, facilitando su mantenimiento y colaboración.
Herramientas de Soporte para PEP 8¶
Uso de herramientas automáticas de verificación de estilo: Herramientas como
flake8,pylintyblackayudan a verificar y aplicar automáticamente el estilo PEP 8 a tu código. Estas herramientas permiten identificar rápidamente problemas de estilo, errores de sintaxis y mejoras potenciales, asegurando que el código cumpla con las convenciones de Python.Flake8: Es una herramienta de verificación de estilo que combina
pyflakes,pycodestyle(anteriormente conocido comopep8) ymccabe. Flake8 revisa el código en busca de errores de sintaxis, incumplimientos del estilo PEP 8, y proporciona advertencias sobre la complejidad del código. Es altamente configurable y puede integrarse fácilmente en editores de texto y sistemas de integración continua (CI).Ventajas: - Detecta errores de sintaxis y problemas de estilo en una sola herramienta. - Soporta la integración con sistemas de CI/CD para automatizar las verificaciones de estilo. - Permite la configuración de reglas personalizadas y extensiones.
flake8 nombre_archivo.pyPylint: Es una herramienta más avanzada que no solo verifica el cumplimiento de PEP 8, sino que también analiza el código en busca de errores, ofrece sugerencias de refactorización, y verifica la calidad del código, incluyendo la detección de código no utilizado y la conformidad con las convenciones de nombres. Pylint genera un informe detallado con una puntuación de calidad del código.
Ventajas: - Ofrece una evaluación integral del código, cubriendo estilo, errores, y calidad. - Altamente configurable para ajustarse a las necesidades específicas del proyecto. - Proporciona sugerencias detalladas para mejorar el código.
pylint nombre_archivo.pyBlack: Es un formateador de código automático que aplica estrictamente las guías de estilo de PEP 8. Black reformatea automáticamente tu código, asegurando que cumple con las convenciones de estilo de Python, lo que reduce el tiempo dedicado a las discusiones de estilo en los equipos de desarrollo y mejora la coherencia del código.
Ventajas: - Automatiza el formateo del código, eliminando la necesidad de revisión manual del estilo. - Aumenta la productividad al reducir las diferencias de estilo en los equipos de desarrollo. - Fácil de usar y se integra bien con los editores de texto modernos.
black nombre_archivo.py
Otras Herramientas Útiles
isort: Ordena automáticamente las importaciones en tu código según las guías de estilo de PEP 8. Mantener las importaciones ordenadas mejora la legibilidad del código y reduce los conflictos durante la fusión de ramas.
isort nombre_archivo.py
Integración con Editores de Texto
Integración en editores de texto: Muchos editores de texto modernos, como Visual Studio Code (VSCode), permiten integrar estas herramientas a través de plugins o extensiones, facilitando su uso directo mientras escribes código. Estas integraciones pueden proporcionar retroalimentación en tiempo real, como resaltar problemas de estilo, sugerir correcciones automáticas y formatear el código al guardar el archivo.
Visual Studio Code (VSCode): - Flake8 y Pylint: Puedes instalar extensiones como «Python» que integran flake8 y pylint para verificar el estilo y errores mientras escribes código. Esto te permite identificar y corregir problemas de manera inmediata. - Black: Existen extensiones que te permiten usar black para formatear automáticamente tu código cada vez que guardas un archivo, asegurando que todo tu código cumpla con las normas de PEP 8 sin esfuerzo adicional. - isort: También se puede integrar para que las importaciones sean ordenadas automáticamente según las convenciones de estilo.
Estas integraciones mejoran significativamente la eficiencia y la calidad del código, al permitirte centrarte en la lógica y el diseño, mientras las herramientas manejan los aspectos de estilo.
Beneficios de Usar Herramientas Automáticas
Al utilizar estas herramientas automáticas, no solo se garantiza que el código cumpla con las guías de PEP 8, sino que también se mejora la eficiencia del equipo, se reduce el tiempo dedicado a revisar problemas de estilo, y se minimizan los errores comunes. Estas herramientas ayudan a:
Mejorar la coherencia: Aseguran que todo el código dentro de un proyecto sigue un estándar uniforme.
Aumentar la productividad: Elimina el esfuerzo manual de revisar el estilo del código, permitiendo que los desarrolladores se concentren en la lógica y la funcionalidad.
Facilitar la colaboración: Un código coherente y bien documentado es más fácil de entender, revisar y mantener por diferentes miembros del equipo.
Al seguir las guías de PEP 8 y utilizar herramientas de soporte automáticas, hacemos que nuestro código sea más legible y profesional, facilitamos la colaboración en equipo y garantizamos su mantenimiento a largo plazo.
Principios de Diseño de Código Limpio¶
El diseño de código limpio se centra en crear un código que sea fácil de leer, entender y mantener. Seguir principios de código limpio no solo mejora la calidad del código, sino que también facilita la colaboración en equipo, reduce el número de errores y simplifica el proceso de desarrollo a largo plazo.
Principios de Código Limpio¶
Mantener funciones cortas y con una sola responsabilidad:
Una función debe hacer solo una cosa y hacerla bien. Esto facilita entender qué hace la función, probarla y reutilizarla. Además, seguir este principio ayuda a identificar problemas más rápidamente y a reducir la complejidad general del código.
Ejemplo:
En lugar de tener una función que maneje múltiples tareas (como obtener datos, procesarlos y mostrarlos), es preferible dividir cada tarea en funciones más pequeñas y específicas.
Uso de nombres descriptivos:
Utiliza nombres claros y significativos para funciones, variables y clases. Un buen nombre debe indicar la intención y el propósito del elemento de código, haciendo que sea fácil de entender sin necesidad de comentarios adicionales.
Ejemplo:
En lugar de nombres genéricos como
x,y, odoStuff(), usa nombres comonumero_estudiantes,calcular_promedio()oobtener_datos_usuario().Evitar repetición de código (DRY: Don’t Repeat Yourself):
El código repetido puede ser difícil de mantener y propenso a errores. Si encuentras que el mismo fragmento de código se repite en varias partes de tu programa, considera crear una función o clase que encapsule esa lógica.
Ejemplo Práctico de Refactorización¶
A continuación se muestra un ejemplo de código antes de la refactorización (código «sucio») y después de la refactorización siguiendo los principios de código limpio.
Código Antes de la Refactorización
Este es un ejemplo de código que no sigue los principios de código limpio:
def procesar_datos_y_mostrar(usuarios):
# Obtener datos
for usuario in usuarios:
nombre = usuario[0]
edad = usuario[1]
print(f'Nombre: {nombre}, Edad: {edad}')
if edad >= 18:
print(f'{nombre} es mayor de edad.')
else:
print(f'{nombre} es menor de edad.')
# Calcular promedio de edades
suma_edades = 0
for usuario in usuarios:
suma_edades += usuario[1]
promedio_edad = suma_edades / len(usuarios)
print(f'Promedio de edad: {promedio_edad}')
Problemas Identificados
Múltiples responsabilidades en una sola función: La función
procesar_datos_y_mostrarrealiza varias tareas: obtener datos, mostrar información, y calcular el promedio de edad.Nombres poco claros:
usuarioses un nombre descriptivo, pero la funciónprocesar_datos_y_mostrarpodría ser más específica.Repetición de código: El bucle que recorre los usuarios aparece dos veces, lo que puede llevar a errores si uno de los bucles se modifica pero el otro no.
Código Después de la Refactorización
Refactorizamos el código para seguir los principios de código limpio:
def mostrar_informacion_usuario(nombre, edad):
"""
Muestra la información de un usuario.
"""
print(f'Nombre: {nombre}, Edad: {edad}')
if edad >= 18:
print(f'{nombre} es mayor de edad.')
else:
print(f'{nombre} es menor de edad.')
def calcular_promedio_edad(usuarios):
"""
Calcula el promedio de edad de una lista de usuarios.
Args:
usuarios (list): Lista de tuplas con información del usuario.
Returns:
float: El promedio de edad.
"""
suma_edades = sum(usuario[1] for usuario in usuarios)
return suma_edades / len(usuarios)
def procesar_usuarios(usuarios):
"""
Procesa y muestra la información de una lista de usuarios.
"""
for usuario in usuarios:
mostrar_informacion_usuario(usuario[0], usuario[1])
promedio_edad = calcular_promedio_edad(usuarios)
print(f'Promedio de edad: {promedio_edad}')
Mejoras Introducidas
Funciones con una sola responsabilidad: -
mostrar_informacion_usuario: Solo se encarga de mostrar la información del usuario. -calcular_promedio_edad: Solo calcula el promedio de edad. -procesar_usuarios: Coordina el procesamiento de usuarios y llama a las funciones específicas.Uso de nombres descriptivos: - Nombres como
mostrar_informacion_usuarioycalcular_promedio_edadexplican claramente lo que hacen.Eliminación de la repetición de código: - Se ha eliminado la repetición del bucle
foral consolidar la lógica de cálculo de promedio en su propia función.