Tabla de Contenidos
Código legible y mantenible
Estilo, diseño, refactorización y herramientas de calidad.
Este módulo forma parte del curso de programación desde cero con Python
1. Título y alcance
Título: Código legible y mantenible: estilo, diseño, refactorización y herramientas de calidad.
Alcance: Aplicar las convenciones de PEP 8 (diseño, nombres, imports, comentarios) y PEP 257 (docstrings); elegir nombres descriptivos y responsabilidades bien definidas; evitar duplicación y complejidad innecesaria (DRY y KISS); sustituir valores mágicos por constantes; escribir comentarios que aporten información; comprender los principios SOLID y su aplicación en Python; refactorizar código sin cambiar su comportamiento; usar Ruff como linter y formateador; y participar en revisiones de código constructivas. Se presupone todo el curso hasta el Módulo 15, en particular funciones (Módulo 9), clases (Módulo 14) y pruebas (Módulo 10). No se introducen herramientas de integración continua ni configuración avanzada de empaquetado (Módulo 19).
2. Objetivos de aprendizaje
Al terminar este módulo serás capaz de:
1. Aplicar las recomendaciones de PEP 8 (indentación, longitud de línea, imports, espacios, nombres) de forma consciente y justificada. 2. Escribir docstrings siguiendo PEP 257 para módulos, clases, funciones y métodos públicos. 3. Elegir nombres descriptivos que revelen la intención del código. 4. Asignar una responsabilidad clara a cada función, clase y módulo. 5. Detectar y eliminar duplicación (DRY) y complejidad innecesaria (KISS). 6. Sustituir valores mágicos por constantes con nombre. 7. Escribir comentarios útiles que expliquen el porqué, no el qué evidente. 8. Explicar los principios SOLID y aplicarlos cuando aporten valor en Python. 9. Refactorizar código existente preservando su comportamiento y apoyándote en pruebas. 10. Configurar y usar Ruff como linter y formateador en un proyecto Python. 11. Participar en revisiones de código con criterios objetivos y feedback constructivo.
3. Conocimientos previos
- Del Módulo 9: Funciones, return, docstrings y funciones puras.
- Del Módulo 10: Pruebas con unittest y diseño de casos de prueba (necesario para refactorizar con seguridad).
- Del Módulo 11: Excepciones y logging.
- Del Módulo 14: Clases, métodos, @property, herencia y composición (necesario para SOLID).
- Del Módulo 15: Comprensiones, generadores y código Pythonic.
- Ningún conocimiento de PEP 8, PEP 257, Ruff ni refactorización sistemática.
4. Introducción motivadora: el código que nadie quiere tocar
Imagina que heredas un proyecto con funciones de 200 líneas, nombres como x, datos2 y procesar(), variables mágicas como if edad > 17: repartidas por todas partes, y bloques de código copiados y pegados con pequeñas variaciones. Cada vez que necesitas cambiar algo, tardas horas en entender qué hace cada pieza y tienes miedo de romper algo. Ese es el coste real de un código poco mantenible.
La buena noticia es que la legibilidad no es un don: es el resultado de convenciones (PEP 8, PEP 257), principios de diseño (DRY, KISS, SOLID) y herramientas (Ruff) que automatizan lo mecánico. Este módulo te enseña a aplicar los tres niveles: el estilo (cómo se escribe), el diseño (cómo se estructura) y el proceso (cómo se mejora y se revisa).
5. Explicación paso a paso
5.1. PEP 8: la guía de estilo oficial
PEP 8 es el documento que define las convenciones de estilo para el código de la biblioteca estándar de Python. No es una ley, pero se ha convertido en el estándar de facto para la comunidad. Su principio rector aparece en el propio documento: “El código se lee mucho más a menudo de lo que se escribe”. Y añade una idea clave: “Una consistencia tonta es el duende de las mentes pequeñas”; es decir, las reglas están al servicio de la legibilidad, no al revés. Si una regla hace el código menos legible en un contexto concreto, se puede (y se debe) desviar.
Puntos esenciales de PEP 8:
| Aspecto | Recomendación |
|---|---|
| Indentación | 4 espacios por nivel. Nunca tabulaciones. |
| Longitud de línea | Máximo 79 caracteres para código, 72 para docstrings y comentarios. |
| Líneas en blanco | Dos líneas en blanco alrededor de funciones y clases de nivel superior; una línea entre métodos. |
| Imports | Uno por línea, agrupados: biblioteca estándar, terceros, propios. Evitar from modulo import *. |
| Espacios | Sin espacio dentro de paréntesis o antes de comas; un espacio alrededor de operadores binarios. |
| Nombres | snake_case para funciones y variables; CapWords para clases; MAYUSCULAS_CON_GUIONES para constantes. |
[BUENA PRÁCTICA] PEP 8 también recomienda usar f-strings (Módulo 5), comprensiones cuando sean claras (Módulo 15) yis Noneen lugar de== None(Módulo 6). No es solo estética: muchas de estas convenciones evitan errores sutiles.
[ERROR HABITUAL] Intentar cumplir PEP 8 al pie de la letra en código heredado que no lo sigue. PEP 8 es explícito: la consistencia dentro de un proyecto es más importante que la consistencia con la guía. Si trabajas en un archivo que usa comillas simples, usa comillas simples, aunque PEP 8 prefiera dobles.
5.2. PEP 257: docstrings
Una docstring es una cadena literal que aparece como primera instrucción de un módulo, función, clase o método. Se convierte en el atributo __doc__ del objeto y es la base de la documentación generada automáticamente.
Reglas principales de PEP 257:
- Usa siempre triple comilla doble ("""). Si hay barras invertidas, usa r""".
- Docstring de una línea: frase imperativa que termina en punto (“Devuelve…”, “Calcula…”). No repitas la firma de la función.
- Docstring multilínea: primera línea como resumen, línea en blanco, y luego la descripción detallada. La comilla de cierre va en su propia línea.
- Todas las funciones y clases públicas deben tener docstring. Los módulos también.
def calcular_media(notas): """Devuelve la media aritmética de una lista de notas. Args: notas: Una lista de números (int o float). Returns: La media como float. Si la lista está vacía, devuelve 0. """ if not notas: return 0 return sum(notas) / len(notas)
[PYTHON] PEP 257 no impone un formato de sección (Args, Returns, Raises). Ese formato proviene de herramientas como Sphinx o Google Style, pero la estructura básica (resumen + detalle) sí es de PEP 257. Elige un formato y sé consistente en todo el proyecto.
5.3. Nombres descriptivos
Un nombre debe responder a la pregunta “¿qué es esto?” o “¿qué hace?”. x no dice nada; precio_final sí. procesar() es vago; calcular_total_con_iva() es explícito.
Reglas prácticas:
- Usa sustantivos para variables y atributos (usuario, saldo_pendiente).
- Usa verbos para funciones y métodos (calcular, validar, enviar).
- Evita abreviaturas crípticas (temp puede ser temperatura; cnt puede ser contador).
- No uses nombres que difieran solo en mayúsculas (edad y Edad son distintos en Python).
- Sé consistente: si usas obtener en un sitio, no uses get en otro para la misma operación.
[BUENA PRÁCTICA] Un buen nombre elimina la necesidad de un comentario. Si necesitas comentar qué hace una variable, probablemente el nombre no es lo bastante descriptivo. El Zen de Python lo resume: “Explícito es mejor que implícito”.
5.4. Responsabilidades bien definidas
Cada función, clase y módulo debe tener una sola responsabilidad. Una función que calcula, imprime y guarda en un archivo está haciendo tres cosas; si cambia cualquiera de ellas, tienes que tocarla. Divídela en tres funciones pequeñas.
Señales de que una función tiene demasiadas responsabilidades:
- Su nombre contiene “y” (calcular_y_mostrar).
- Tiene muchos parámetros o variables locales.
- Es difícil de probar sin depender de la entrada/salida.
- Ocupa más de 20-30 líneas.
[BUENA PRÁCTICA] Aplica el principio de responsabilidad única ya en el diseño (Módulo 10). Si una función necesita más de una frase para describir qué hace, divídela.
5.5. DRY y KISS
DRY (Don't Repeat Yourself): cada pieza de conocimiento debe tener una representación única y autoritativa en el sistema. Si la misma lógica aparece en tres lugares, un cambio futuro requerirá tres modificaciones y es fácil olvidar una.
KISS (Keep It Simple, Stupid): la solución más simple que funcione suele ser la mejor. La complejidad innecesaria (abstracciones prematuras, patrones rebuscados) encarece el mantenimiento.
# DRY: una función reutilizable en lugar de tres bloques copiados def calcular_precio_con_iva(precio, iva=0.21): return precio * (1 + iva)
[ERROR HABITUAL] Abusar de DRY y crear abstracciones para código que solo se repite dos veces y que probablemente evolucione de forma distinta. La regla de tres sugiere esperar a la tercera repetición antes de abstraer. La simplicidad (KISS) también cuenta.
5.6. Constantes y valores mágicos
Un valor mágico es un número o cadena literal que aparece en el código sin explicación: if edad > 18:, precio * 1.21, "admin". Si ese valor cambia o si su significado no es evidente, el código se vuelve frágil.
Solución: define una constante con nombre en mayúsculas al principio del módulo o de la clase.
MAYORIA_DE_EDAD = 18 TIPO_IVA = 0.21 ROL_ADMIN = "admin" if edad >= MAYORIA_DE_EDAD: ...
[BUENA PRÁCTICA] PEP 8 recomienda nombres enMAYUSCULAS_CON_GUIONES_BAJOSpara constantes. No todas las “constantes” de Python son inmutables (el lenguaje no lo impide), pero la convención comunica la intención.
5.7. Comentarios útiles
Un comentario debe explicar el porqué, no el qué. # Incrementa x en 1 es inútil si la línea es x += 1. # Reintentamos porque la API a veces devuelve un error temporal sí aporta información que el código no revela.
Tipos de comentarios según PEP 8:
- Comentarios de bloque: líneas completas que explican un fragmento. Empiezan con # y suelen limitarse a 72 caracteres.
- Comentarios en línea: al final de una instrucción, separados por al menos dos espacios y empezando con # .
[ERROR HABITUAL] Comentar código obsoleto en lugar de eliminarlo. Si usas Git (Módulo 19), el historial conserva la versión anterior. El código comentado se queda obsoleto, confunde y acumula ruido.
5.8. Principios SOLID
SOLID es un acrónimo de cinco principios de diseño orientado a objetos. No son específicos de Python, pero se aplican perfectamente y ayudan a estructurar clases mantenibles.
| Principio | Idea central | En Python |
|---|---|---|
| S — Single Responsibility | Una clase, una razón para cambiar. | Separa Usuario (datos), UsuarioRepository (persistencia) y EmailService (notificaciones). |
| O — Open/Closed | Abierta a extensión, cerrada a modificación. | Usa clases base o protocolos; añade subclases en lugar de modificar if/elif interminables. |
| L — Liskov Substitution | Las subclases deben poder sustituir a la superclase sin romper el contrato. | Evita herencias que cambian el comportamiento esperado (el clásico Cuadrado/Rectangulo). |
| I — Interface Segregation | Interfaces pequeñas y específicas. | En Python, usa protocolos o clases abstractas con pocos métodos; no obligues a implementar métodos que no se usan. |
| D — Dependency Inversion | Depende de abstracciones, no de detalles. | Inyecta dependencias (pasa un objeto que cumple una interfaz) en lugar de instanciar directamente la implementación concreta. |
[AMPLIACIÓN] SOLID es más útil en proyectos grandes con jerarquías de clases complejas. En scripts pequeños, aplicar los cinco principios puede ser contraproducente (KISS). Úsalos como criterio cuando el diseño empiece a volverse rígido o difícil de probar.
5.9. Refactorización
Refactorizar es mejorar la estructura interna del código sin cambiar su comportamiento externo. No es añadir funcionalidad ni corregir errores: es hacer el código más limpio, más legible y más fácil de modificar.
Pasos seguros para refactorizar:
1. Ten pruebas que cubran el comportamiento actual (Módulo 10). Sin pruebas, refactorizar es apostar. 2. Haz cambios pequeños y ejecuta las pruebas después de cada uno. 3. Empieza por lo más seguro: renombrar variables, extraer funciones, eliminar duplicación. 4. No mezcles refactorización con cambios funcionales en el mismo commit.
Refactorizaciones típicas:
- Extraer función: mover un bloque de código a una función con un nombre descriptivo.
- Renombrar: cambiar un nombre poco claro por uno que revele la intención.
- Eliminar duplicación: unificar bloques repetidos en una función o constante.
- Simplificar condicionales: usar elif en lugar de anidamiento excesivo, o guard clauses.
[BUENA PRÁCTICA] La refactorización es una actividad continua, no una fase separada. Cada vez que tocas una función, déjala un poco mejor de como la encontraste (la “regla del boy scout”).
5.10. Ruff: linter y formateador
Ruff es un linter y formateador de Python escrito en Rust, extremadamente rápido. Reemplaza a múltiples herramientas tradicionales (flake8, black, isort, pyupgrade, pydocstyle, etc.) en una sola.
Instalación:
pip install ruff
Uso básico:
ruff check . # Linter: muestra problemas ruff check --fix . # Linter: corrige automáticamente lo que puede ruff format . # Formateador: reescribe el código según el estilo
Configuración en pyproject.toml:
[tool.ruff]
line-length = 88 # Longitud de línea (Black usa 88 por defecto)
target-version = "py315"
[tool.ruff.lint]
select = [
"E", # pycodestyle (PEP 8)
"F", # pyflakes (nombres no definidos, imports sin usar)
"I", # isort (orden de imports)
"B", # bugbear (errores comunes)
"UP", # pyupgrade (sintaxis moderna)
"RUF", # reglas propias de Ruff
]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
[PYTHON] Ruff soporta las novedades de Python 3.15. Por ejemplo, la reglaRUF017sugiere usar el desempaquetado en comprensiones ([*L for L in listas]) cuando se detecta un patrón equivalente en versiones anteriores. También respeta las importaciones perezosas (lazy import) como casos exentos de ciertas reglas.
[BUENA PRÁCTICA] Integra Ruff en tu editor (VS Code, PyCharm) para que te avise mientras escribes. Y ejecútalo en la terminal antes de cada commit:ruff check --fix . && ruff format .es un flujo de trabajo típico.
5.11. Revisiones de código
Una revisión de código (code review) es la práctica de que otra persona examine tus cambios antes de integrarlos. No es un examen ni una crítica personal: es una conversación técnica para detectar errores, mejorar el diseño y compartir conocimiento.
Qué revisar, por orden de prioridad:
1. Corrección: ¿El código hace lo que debe? ¿Cubre los casos límite? 2. Diseño: ¿Las responsabilidades están bien separadas? ¿Hay duplicación? 3. Legibilidad: ¿Los nombres son claros? ¿Los comentarios aportan? 4. Estilo: ¿Sigue PEP 8? (Esto puede automatizarse con Ruff; no gastes tiempo humano en ello).
Cómo dar feedback constructivo:
- Sé específico: señala la línea y explica por qué algo podría mejorarse. - Distingue entre “debe cambiarse” (corrección, seguridad) y “podría cambiarse” (estilo, preferencia). - Reconoce lo que está bien: una revisión no es solo una lista de defectos.
[BUENA PRÁCTICA] Usa herramientas automáticas (Ruff, pruebas) para lo mecánico, y reserva la revisión humana para lo que requiere criterio: diseño, legibilidad, decisiones de arquitectura.
6. Ejemplo completo comentado: refactorización paso a paso
Código inicial (difícil de mantener):
def procesar(lista): total = 0 for i in range(len(lista)): if lista[i] > 18: total = total + lista[i] print("Total:", total) return total
Problemas detectados:
- Nombre procesar vago; lista poco descriptivo.
- range(len(lista)) en lugar de iterar directamente (Módulo 15).
- Valor mágico 18 sin constante.
- Mezcla cálculo e impresión (dos responsabilidades).
- total = total + ... en lugar de +=.
Refactorización:
MAYORIA_DE_EDAD = 18 def sumar_mayores_de_edad(edades): """Devuelve la suma de las edades que superan la mayoría de edad.""" return sum(edad for edad in edades if edad > MAYORIA_DE_EDAD) def mostrar_total(edades): """Imprime el total de edades que superan la mayoría de edad.""" print("Total:", sumar_mayores_de_edad(edades))
Resultado: dos funciones con nombres claros, una constante con nombre, iteración directa, sin mezcla de cálculo y presentación. El comportamiento externo es el mismo, pero el código es más legible y más fácil de probar.
7. Seguimiento del proceso de refactorización
Refactorizar no es reescribir. El proceso seguro es:
1. Verificar que las pruebas existentes pasan. 2. Aplicar una transformación pequeña (renombrar, extraer, simplificar). 3. Ejecutar las pruebas de nuevo. 4. Repetir con la siguiente transformación.
Si una transformación rompe las pruebas, has cambiado el comportamiento (o las pruebas tenían un error). Vuelve atrás y ajusta. Este ciclo corto es lo que hace que la refactorización sea segura.
8. Errores habituales
| Error | Causa | Cómo evitarlo |
|---|---|---|
Nombres de una letra (x, n, d) | Pereza o prisa. | Usa nombres descriptivos; el autocompletado del editor ayuda. |
| Funciones que hacen demasiado | Falta de descomposición. | Aplica responsabilidad única; extrae funciones. |
| Valores mágicos repetidos | No usar constantes. | Define constantes al principio del módulo. |
| Comentarios que repiten el código | Mala interpretación de su propósito. | Comenta el porqué, no el qué. |
| Refactorizar sin pruebas | Exceso de confianza. | Escribe pruebas antes de tocar el código. |
| Ignorar Ruff o el linter | Prisa o desconocimiento. | Integra Ruff en el editor y en el flujo de trabajo. |
| Revisiones de código centradas solo en estilo | Se automatiza lo mecánico y se descuida el diseño. | Usa Ruff para el estilo y reserva la revisión humana para corrección y diseño. |
| Aplicar SOLID en exceso | Sobreingeniería. | Aplica KISS: los principios son herramientas, no dogmas. |
9. Particularidades de Python frente a otros lenguajes
- PEP 8 no es una ley: en Java, las convenciones de nombres son casi universales (camelCase para métodos, PascalCase para clases); en Python, PEP 8 es una guía con margen para la consistencia del proyecto.
- Docstrings integradas: el atributo __doc__ y help() hacen que la documentación sea de primera clase. En Java, Javadoc requiere un formato específico y herramientas externas.
- Ruff reemplaza varias herramientas: en otros ecosistemas, es común combinar linter, formateador y ordenador de imports por separado. Ruff los unifica en una sola herramienta extremadamente rápida.
- SOLID y duck typing: en Python, la inversión de dependencias puede lograrse sin interfaces explícitas, gracias al duck typing (Módulo 14). Esto simplifica el código, pero exige documentar bien los contratos esperados.
10. Buenas prácticas
Incorrecto (difícil de leer y mantener):
def f(l): t = 0 for i in range(len(l)): if l[i] > 18: t += l[i] print(t)
Mejor:
MAYORIA_DE_EDAD = 18 def sumar_mayores_de_edad(edades): """Devuelve la suma de las edades que superan la mayoría de edad.""" return sum(edad for edad in edades if edad > MAYORIA_DE_EDAD)
[BUENA PRÁCTICA] Prioriza la claridad sobre la brevedad. Un código de 10 líneas legibles es mejor que uno de 3 líneas crípticas. El Zen de Python: “La legibilidad cuenta”. Y recuerda PEP 8: la consistencia dentro del proyecto es más importante que la consistencia con la guía.
11. Windows y Linux
- Ruff funciona igual en Windows y Linux. Se instala con pip y se ejecuta desde la terminal.
- Codificación: con Python 3.15 y UTF-8 por defecto, los archivos con tildes y caracteres especiales se leen y escriben correctamente en ambos sistemas, lo que evita problemas al aplicar formato o al generar documentación.
- Rutas: si configuras Ruff en pyproject.toml, las rutas de exclusión (.venv, __pycache__) se interpretan de forma portable. Usa / en la configuración, que funciona en ambos sistemas.
[WINDOWS / LINUX] Si usas VS Code, la extensión de Ruff funciona igual en ambos sistemas. El formateador y el linter se ejecutan en el mismo entorno virtual, sin diferencias de comportamiento.
12. Ejercicios guiados
Ejercicio guiado 1: Aplicar PEP 8.
Revisa este fragmento y corrige las violaciones de PEP 8 evidentes (indentación, espacios, nombres, imports):
import os,sys def F(x): if x>0: return x*2
Ejercicio guiado 2: Docstring según PEP 257.
Escribe una función es_palindromo(texto) que devuelva True si el texto es un palíndromo (ignorando espacios y mayúsculas). Incluye una docstring de una línea y otra multilínea para una versión más detallada.
Ejercicio guiado 3: Refactorizar con DRY.
El siguiente código tiene duplicación. Refactorízalo para eliminar la repetición:
def area_rectangulo(ancho, alto): return ancho * alto def area_triangulo(base, altura): return (base * altura) / 2 def area_cuadrado(lado): return lado * lado
Pista: ¿Puedes expresar todas en términos de una función más general?
13. Ejercicios autónomos
1. Nombres: Toma una función que hayas escrito en un módulo anterior y renombra sus variables y la propia función para que sean autoexplicativas. Compara el antes y el después.
2. Constantes: Busca en tu código algún valor numérico o cadena literal repetido y sustitúyelo por una constante con nombre.
3. Ruff: Instala Ruff en un proyecto pequeño, ejecuta ruff check . y corrige los problemas que encuentre. Luego ejecuta ruff format . y observa los cambios.
4. SOLID: Crea una clase Pedido que calcule el total, aplique descuentos e imprima la factura. Refactorízala para que cada responsabilidad esté en una clase separada (SRP).
5. Reto opcional: Investiga las reglas de Ruff relacionadas con Python 3.15 (por ejemplo, RUF017 para desempaquetado en comprensiones). Configura pyproject.toml para activarlas y comprueba que Ruff sugiere la sintaxis moderna.
14. Soluciones razonadas
Solución al ejercicio guiado 1:
import os import sys def f(x): if x > 0: return x * 2
Correcciones: imports en líneas separadas; nombre de función en snake_case y descriptivo (aunque f es vago, al menos la sintaxis es correcta); indentación de 4 espacios; espacios alrededor de >; espacio después de la coma en el import.
Solución al ejercicio guiado 2:
def es_palindromo(texto): """Devuelve True si el texto es un palíndromo (ignora espacios y mayúsculas).""" limpio = texto.replace(" ", "").lower() return limpio == limpio[::-1]
Docstring de una línea, en imperativo, con punto final.
Solución al ejercicio guiado 3 (DRY):
def area_rectangulo(ancho, alto): return ancho * alto def area_triangulo(base, altura): return area_rectangulo(base, altura) / 2 def area_cuadrado(lado): return area_rectangulo(lado, lado)
area_triangulo y area_cuadrado reutilizan area_rectangulo, eliminando la duplicación del producto.
Solución al ejercicio 4 (SOLID, orientativa):
class Pedido: def __init__(self, lineas): self.lineas = lineas class CalculadoraTotal: def calcular(self, pedido): return sum(linea.precio * linea.cantidad for linea in pedido.lineas) class AplicadorDescuento: def aplicar(self, total): return total * 0.9 # Ejemplo class FacturaPrinter: def imprimir(self, total): print(f"Total: {total}")
Cada clase tiene una responsabilidad: datos, cálculo, descuento y presentación.
15. Resumen
- PEP 8 define el estilo: indentación de 4 espacios, líneas de 79 caracteres, imports ordenados, nombres en snake_case/CapWords.
- PEP 257 define las docstrings: triple comilla doble, resumen en una línea, detalle separado por línea en blanco.
- Los nombres descriptivos y las responsabilidades únicas son la base de la legibilidad.
- DRY evita duplicación; KISS evita complejidad innecesaria.
- Las constantes sustituyen a los valores mágicos.
- Los comentarios explican el porqué, no el qué.
- SOLID ofrece cinco principios para diseñar clases mantenibles.
- Refactorizar es mejorar la estructura sin cambiar el comportamiento; requiere pruebas.
- Ruff unifica linter y formateador, es rápido y soporta Python 3.15.
- Las revisiones de código son una práctica social para mejorar la calidad y compartir conocimiento.
16. Autoevaluación
1. ¿Por qué PEP 8 dice que “la consistencia dentro de un proyecto es más importante que la consistencia con la guía”? ¿Qué implica en la práctica?
2. ¿Qué diferencia hay entre un comentario útil y uno que repite el código? Pon un ejemplo de cada uno.
3. ¿Qué principio SOLID se viola cuando una clase Usuario se encarga de guardarse en la base de datos y de enviar correos? ¿Cómo lo corregirías?
4. ¿Qué hace ruff check --fix . y en qué se diferencia de ruff format .?
5. Reto: Sin ejecutar, identifica al menos tres problemas de legibilidad en este código y propón una versión refactorizada:
```python
def p(l):
t = 0
for i in range(len(l)):
if l[i] > 18:
t += l[i]
print(t)
return t
```
17. Fuentes oficiales
- PEP 8 – Style Guide for Python Code: https://peps.python.org/pep-0008/
- PEP 257 – Docstring Conventions: https://peps.python.org/pep-0257/
- PEP 20 – The Zen of Python: https://peps.python.org/pep-0020/
- Documentación oficial de Ruff: https://docs.astral.sh/ruff/
- Configuración de Ruff en pyproject.toml: https://docs.astral.sh/ruff/configuration/
- Reglas de Ruff (incluye RUF017 para desempaquetado en comprensiones en 3.15): https://docs.astral.sh/ruff/rules/
- SOLID Design Principles in Python (Real Python): https://realpython.com/solid-principles-python/
- Novedades de Python 3.15 (para contexto de Ruff y sintaxis moderna): https://docs.python.org/3/whatsnew/3.15.html
