Mejores Prácticas
## Objetivos de Aprendizaje
- Escribir código Python limpio y mantenible
- Seguir las pautas de estilo PEP 8
- Dominar técnicas de depuración
- Escribir pruebas efectivas
## Estilo de Código
### Pautas PEP 8
- Usa 4 espacios por nivel de indentación
- Las líneas deben tener ≤ 79 caracteres (límite suave)
- Dos líneas en blanco entre definiciones de nivel superior
- Una línea en blanco entre definiciones de métodos en una clase
- Sin espacios alrededor de `=` para argumentos de palabra clave o valores por defecto
```python
# Bien
def saludar(nombre, saludo="Hola"):
return f"{saludo}, {nombre}!"
# Mal
def saludar ( nombre, saludo = "Hola" ) :
return saludo + ", " + nombre + "!"
```
### Convenciones de Nomenclatura
```python
# Variables y funciones: snake_case
nombre_usuario = "Ana"
def calcular_total():
pass
# Clases: PascalCase
class CuentaUsuario:
pass
# Constantes: UPPER_SNAKE_CASE
MAX_REINTENTOS = 3
API_URL = "https://api.ejemplo.com"
# Privado: prefijo con guion bajo
_funcion_privada()
_variable_instancia
```
### Imports
```python
# Biblioteca estándar primero
import os
import sys
# Terceros
import requests
from flask import Flask
# Aplicación local
from mipaquete import mimofulo
# Absoluto vs Relativo
from paquete import modulo # Preferido
from . import modulo # OK para relativo
import modulo # Evitar relativo
```
## Funciones
### Responsabilidad Única
```python
# Bien - cada función hace una cosa
def validar_email(email):
"""Verificar si el email es válido."""
return "@" in email and "." in email
def enviar_email(destinatario, mensaje):
"""Enviar un email."""
if not validar_email(destinatario):
raise ValueError("Email inválido")
# Lógica de envío...
# Mal - la función hace demasiado
def enviar_email(destinatario, mensaje):
# Valida, envía, registra, reintenta...
```
### Retornos Tempranos
```python
# Bien - retorno temprano para guard clauses
def procesar(usuario):
if usuario is None:
return None
if not usuario.esta_activo:
return None
return usuario.procesar()
# Mal - anidado profundamente
def procesar(usuario):
if usuario is not None:
if usuario.esta_activo:
return usuario.procesar()
else:
return None
else:
return None
```
### Docstrings
```python
def sumar(a, b):
"""
Suma dos números.
Args:
a: Primer número
b: Segundo número
Returns:
La suma de a y b
Raises:
TypeError: Si a o b no es un número
"""
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Los argumentos deben ser números")
return a + b
```
## Clases
### Usar Properties
```python
# Bien - usar property para acceso controlado
class CuentaBancaria:
def __init__(self, saldo):
self._saldo = saldo
@property
def saldo(self):
return self._saldo
@saldo.setter
def saldo(self, valor):
if valor < 0:
raise ValueError("El saldo no puede ser negativo")
self._saldo = valor
```
### dataclasses para Clases Simples
```python
from dataclasses import dataclass
@dataclass
class Punto:
x: float
y: float
def distancia_desde_origen(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
```
## Manejo de Errores
### Excepciones Específicas
```python
# Bien
try:
valor = int(entrada_usuario)
except ValueError:
print("Por favor introduce un número válido")
# Mal - captura todo
try:
valor = int(entrada_usuario)
except:
print("Error")
```
### Jerarquía de Excepciones
```python
# Crear excepciones específicas
class ValidationError(Exception):
pass
class InvalidEmailError(ValidationError):
pass
class InvalidPasswordError(ValidationError):
pass
```
### No Suprimir Excepciones
```python
# Mal
try:
hacer_algo()
except:
pass
# Bien - al mínimo, registrarla
try:
hacer_algo()
except Exception as e:
logging.error(f"Falló: {e}")
raise
```
## Sugerencias de Tipo
```python
from typing import List, Dict, Optional, Union
def procesar(numeros: List[int]) -> Dict[str, int]:
return {
"cantidad": len(numeros),
"suma": sum(numeros),
}
def encontrar_usuario(id_usuario: int) -> Optional[str]:
if id_usuario == 0:
return None
return "Usuario encontrado"
def parsear(valor: Union[str, int]) -> int:
return int(valor)
```
## Rendimiento
### List Comprehensions sobre Bucles
```python
# Bien
cuadrados = [x ** 2 for x in range(100)]
# Mal
cuadrados = []
for x in range(100):
cuadrados.append(x ** 2)
```
### Generadores para Datos Grandes
```python
# Bien - eficiente en memoria
def leer_archivo_grande(nombre_archivo):
with open(nombre_archivo) como f:
for linea in f:
yield linea
# Mal - carga todo el archivo
def leer_archivo_grande(nombre_archivo):
with open(nombre_archivo) como f:
return f.readlines()
```
### Cacheo de Variables Locales
```python
import math
# Más lento - búsqueda de atributo repetida
def calcular(numeros):
resultado = []
for num in numeros:
resultado.append(math.sin(num) + math.cos(num))
return resultado
# Más rápido - cachear funciones math
def calcular(numeros):
resultado = []
sin, cos = math.sin, math.cos
for num in numeros:
resultado.append(sin(num) + cos(num))
return resultado
```
## Conceptos Básicos de Pruebas
### Usando unittest
```python
import unittest
class TestMatematica(unittest.TestCase):
def test_sumar(self):
self.assertEqual(1 + 1, 2)
def test_dividir(self):
self.assertRaises(ZeroDivisionError, lambda: 1 / 0)
if __name__ == "__main__":
unittest.main()
```
### Usando pytest
```python
import pytest
def test_sumar():
assert 1 + 1 == 2
def test_dividir():
with pytest.raises(ZeroDivisionError):
1 / 0
def test_list_comprehension():
resultado = [x ** 2 for x in range(5)]
assert resultado == [0, 1, 4, 9, 16]
```
## Depuración
### Depuración con print()
```python
def funcion_con_error(x):
print(f"DEBUG: x = {x}") # Añadir salida de depuración
return x * 2
```
### assert
```python
def procesar(edad):
assert edad >= 0, "La edad no puede ser negativa"
assert isinstance(edad, int), "La edad debe ser un entero"
# Procesar...
```
### Usando pdb
```python
import pdb
def funcion_con_error(x):
pdb.set_trace() # Punto de interrupción
return x * 2
```
## Resumen
- Seguir las pautas de estilo PEP 8
- Usar nombres de variables y funciones significativos
- Mantener funciones pequeñas y enfocadas
- Usar sugerencias de tipo para documentación
- Manejar excepciones específicamente
- Usar list comprehensions sobre bucles
- Usar generadores para grandes conjuntos de datos
- Escribir pruebas para código crítico
- Usar aserciones para depuración
- Mantener código DRY (Don't Repeat Yourself)
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →