En el desarrollo con Python, a pesar de su sintaxis intuitiva, es común enfrentar errores que pueden causar confusión. A continuación, se exploran algunas trampas habituales, agrupadas temáticamente, con ejemplos alternativos y explicaciones detalladas.
- Confusión en el ámbito de las variables
Ejemplo problemático:
punto = 8
def obtener_valor():
print(punto) # Provoca UnboundLocalError
punto = 3
obtener_valor()
Causa: Python interpreta las variables asignadas dentro de una función como locales. Si se hace referencia antes de la asignación, se genera un error.
Solución: Emplear declaraciones como global o nonlocal cuando sea necesario, o evitar reasignar variables con el mismo nombre en el contexto local.
- Uso de objetos mutables como valores predeterminados
Ejemplo incorrecto:
def insertar_elemento(valor, conjunto=[]):
conjunto.append(valor)
return conjunto
print(insertar_elemento(10)) # [10]
print(insertar_elemento(20)) # [10, 20] — Se esperaba [20]
Razón: Los parámetros por defecto se evalúan una sola vez en la definición de la función, compartiéndose entre llamadas posteriores.
Corrección: Utilizar None como valor inicial y crear una nueva lista dentro de la función:
def insertar_elemento(valor, conjunto=None):
if conjunto is None:
conjunto = []
conjunto.append(valor)
return conjunto
- Diferencia entre listas por comprensión y generadores
Error típico:
cuadrados = (n**2 for n in range(4))
print(list(cuadrados)) # [0, 1, 4, 9]
print(list(cuadrados)) # [] — El generador ya se agotó
Motivo: Los generadores son iteradores de un solo uso.
Alternativa: Convertir a lista si se necesita reutilizar los datos, o regenerar el generador cada vez.
- Copias superficiales frente a copias profundas
Escenario que causa problemas:
import copy
datos = [[1, 2], [3, 4]]
copia = copy.copy(datos)
copia[0][0] = 50
print(datos) # [[50, 2], [3, 4]] — La estructura original se modificó
Explicación: Una copia superficial solo duplica el contenedor externo; los objetos internos siguen siendo referencias.
Remedio: Usar copy.deepcopy() para obtener una copia independiente.
- Rendimiento en concatenación de cadenas
Ejemplo ineficinete:
acumulador = ""
for fragmento in fragmentos:
acumulador += str(fragmento) # Crea nuevos objetos string en cada iteración
Problema: Las cadenas son inmutables, y el operador += genera una nueva cadena en cada paso, impactando el rendimiento.
Mejor práctica: Usar el método ''.join(lista_de_cadenas) para mayor eficiencia.
- Manejo inadecuado de excepciones
Antipatrón común:
try:
operacion_arriesgada()
except: # Captura cualquier excepción, incluyendo SystemExit o KeyboardInterrupt
pass
Consecuencia: Bloquear errores específicos y dificultar la interrupción del programa.
Recomendación: Capturar excepciones concretas, como except ValueError:.
- Referencias vs. copias en objetos mutables
Error conceptual:
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4] — Ambas variables apuntan al mismo objeto
Causa: La asignación b = a crea una referencia, no una copia.
Solución: Generar una copia con b = a.copy() o b = a[:].
- Diferencias entre operadores de identidad y valor
Confusión típica:
x = [5, 6]
y = [5, 6]
print(x == y) # True — Comparación de valores
print(x is y) # False — No son el mismo objeto en memoria
Nota: == verifica igualdad de valores, mientras que is verifica si son el mismo objeto. No confiar en is para tipos primitivos, ya que Python puede usar caché.
- Modificar listas durante su iteración
Problema durante el bucle:
elementos = [1, 2, 3, 4, 5]
for elem in elementos:
if elem % 2 == 0:
elementos.remove(elem) # Salta elementos al modificar durante el recorrido
Corrección: Usar listas por comprensión o iterar en orden inverso:
elementos = [x for x in elementos if x % 2 != 0]
- Codificación en operaciones de archivos
Omisión frecuente:
with open('archivo.txt') as fichero: # Depende del sistema operativo
contenido = fichero.read()
Riesgo: Errores de decodificación en entornos mixtos o con caracteres especiales.
Práctica segura: Especificar la codificación explícitamente:
with open('archivo.txt', encoding='utf-8') as fichero:
contenido = fichero.read()
- Organización de paquetes con __init__.py
Problema habitual: No crear el archivo __init__.py en directorios de paquetes puede impedir la importación. Aunque Python 3.3+ permite paquetes de espacio de nombres implícitos, es recomendable incluirlo para mayor claridad.
- Enlace de variables en funciones lambda dentro de bucles
Ejemplo engañoso:
funciones = [lambda x: x * i for i in range(3)]
print([f(2) for f in funciones]) # [4, 4, 4] — Se esperaba [0, 2, 4]
Razón: La variable i se evalúa al momento de la llamada, no de la definición.
Ajuste: Capturar el valor actual con un parámetro por defecto:
funciones = [lambda x, valor=i: x * valor for i in range(3)]
Para mejorar la calidad del código, se sugiere usar anotaciones de tipo (por ejemplo, def funcion(par: int) -> str:) y herramientas estáticas como pylint o mypy. Además, consultar la documentación oficial y guías de estilo como PEP 8 es fundamental.