Python

Problemas con listas mutables en Python: Cómo evitar efectos inesperados

Las listas en Python son estructuras de datos mutables, lo que significa que su contenido puede modificarse después de su creación. Esta característica las diferencia de las tuplas, que son inmutables y no permiten cambios una vez definidas. Mientras que la mutabilidad de las listas ofrece gran flexibilidad, también puede generar efectos inesperados si no se gestionan de forma adecuada. Problemas como modificaciones no deseadas, referencias compartidas y errores en las funciones que reciben listas como argumentos son comunes al trabajar con listas mutables.

En esta entrada, abordaremos algunos de los errores más frecuentes que pueden aparecer al trabajar con listas mutables en Python y explicaremos cómo evitarlos para lograr un código más robusto, predecible y fácil de mantener.

¿Qué son las listas mutables en Python?

En Python, los objetos pueden ser mutables o inmutables:

  • Mutables: Son aquellos cuyo contenido puede modificarse después de su creación. Ejemplos: listas, diccionarios, conjuntos.
  • Inmutables: Son aquellos cuyo contenido no puede modificarse después de su creación. Ejemplos: tuplas, cadenas de texto, números (enteros, flotantes).
mi_lista = [1, 2, 3]
mi_lista.append(4)  # Modificamos la lista original

print(mi_lista)
[1, 2, 3, 4]

Problemas comunes al trabajar con listas mutables

Aquí te mostramos algunos de los errores más comunes y cómo evitarlos para que tu código sea más seguro y fácil de mantener.

Modificación inesperada al asignar listas

En Python, cuando asignas una lista a otra variable, ambas referencias apuntan al mismo objeto en memoria. Es decir, hay una única lista en memoria con dos nombres. Esto puede llevar a modificaciones no deseadas si una de las listas es modificada.

lista_a = [1, 2, 3]
lista_b = lista_a  # Ambas variables apuntan a la misma lista

lista_b.append(4)  # Modificamos lista_b
print(lista_a)  # La lista_a también cambió
[1, 2, 3, 4]

Solución: Para evitar este comportamiento, utiliza métodos como copy() o deepcopy() para crear copias independientes de la lista:

lista_a = [1, 2, 3]
lista_b = lista_a.copy()  # Ahora lista_b es una copia independiente

lista_b.append(4)
print(lista_a)  # [1, 2, 3] (No se modifica)
print(lista_b)  # [1, 2, 3, 4]
[1, 2, 3]
[1, 2, 3, 4]

Si la lista contiene elementos mutables, como sublistas, es recomendable usar deepcopy():

import copy

lista_anidada = [[1, 2], [3, 4]]
copia_profunda = copy.deepcopy(lista_anidada)

Mutabilidad en funciones: Modificaciones no deseadas

Cuando pasamos una lista como argumento a una función, esta puede modificarse dentro de la función, lo cual puede producir efectos indeseados si no se toma precaución.

def agregar_elemento(lista):
    lista.append(100)  # Modifica la lista original


mi_lista = [1, 2, 3]
agregar_elemento(mi_lista)
print(mi_lista)  # Se modificó inesperadamente
[1, 2, 3, 100]

Solución: Para evitar este problema, haz una copia de la lista dentro de la función antes de modificarla:

def agregar_elemento(lista):
    lista_copia = lista.copy()  # Copia de la lista
    lista_copia.append(100)
    return lista_copia


mi_lista = [1, 2, 3]
nueva_lista = agregar_elemento(mi_lista)
print(mi_lista)     # No cambia
print(nueva_lista)  # Nueva lista con un elemento
[1, 2, 3]
[1, 2, 3, 100]

Problemas con valores predeterminados mutables en funciones

Una de las trampas más sutiles en Python ocurre cuando usas una lista mutable como valor predeterminado de un parámetro de función. En este caso, todas las llamadas sucesivas compartirán la misma lista.

def agregar_valor(valor, lista=[]):
    lista.append(valor)
    return lista


print(agregar_valor(1))  # [1]
print(agregar_valor(2))  # [1, 2] (Acumula valores inesperadamente)
print(agregar_valor(3))  # [1, 2, 3]
[1]
[1, 2]
[1, 2, 3]

Solución: Una forma de evitar este comportamiento es usar None como valor predeterminado y crear una nueva lista dentro de la función:

def agregar_valor(valor, lista=None):
    if lista is None:
        lista = []  # Se crea una nueva lista en cada llamada
    lista.append(valor)
    return lista


print(agregar_valor(1))  # [1]
print(agregar_valor(2))  # [2] (Ya no acumula valores inesperados)
[1]
[2]

Mutabilidad en estructuras anidadas

Si una lista mutable contiene otras listas, cambiar una sublista afectará todas las referencias a esa sublista. Este problema puede ser difícil de detectar si no se presta atención.

matriz = [[0] * 3] * 3  # Crea 3 referencias al mismo objeto
matriz[0][0] = 1

print(matriz) # Todas las filas cambiaron
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]

Solución: Usa comprensión de listas para crear copias independientes de cada sublista:

matriz = [[0] * 3 for _ in range(3)]  # Cada fila es una lista independiente
matriz[0][0] = 1

print(matriz)
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

Conclusiones

Las listas mutables en Python son una herramienta poderosa y flexible, pero pueden llevar a errores difíciles de detectar si no se manejan adecuadamente. Siguiendo las buenas prácticas aquí descritas, puedes evitar problemas comunes como referencias compartidas, modificaciones no deseadas y errores complicados en funciones. Asegúrate de crear copias adecuadas cuando sea necesario y ten en cuenta la mutabilidad de las estructuras de datos para escribir un código más predecible y fácil de depurar.

Nota: La imagen de este artículo fue generada utilizando un modelo de inteligencia artificial.

¿Te ha parecido de utilidad el contenido?

Daniel Rodríguez

Share
Published by
Daniel Rodríguez

Recent Posts

BG/BB: el modelo de CLV para compras en períodos discretos

Los dos artículos anteriores de la serie cubrieron BG/NBD, el modelo para negocios donde el…

56 segundos ago

Por Qué las Contraseñas Largas son más Seguras que las Complejas

"Tu contraseña debe tener al menos 8 caracteres, una mayúscula, un número y un símbolo…

5 días ago

Gini, KS y AUC: Cómo Medir la Calidad de un Modelo de Credit Scoring

Cuando un analista de riesgo presenta un nuevo scorecard ante el comité de dirección, la…

1 semana ago

Curiosidad: De dónde vienen foo, bar y foobar

Si revisáis código o documentación de otros programadores, tarde o temprano os toparéis con dos…

2 semanas ago

BG/NBD + Gamma/Gamma: el modelo probabilístico para CLV en compras continuas

Los dos artículos anteriores cubrieron el CLV clásico —una fórmula útil pero que trata a…

2 semanas ago

Errores habituales al trabajar con DataFrames grandes en Pandas (y cómo evitarlos)

Hay un momento muy concreto en la vida de casi todo análisis: el código que…

3 semanas ago

This website uses cookies.