Argumentos Mutables por Defecto
Cuando combinas argumentos mutables con argumentos por defecto en Python, puede llevar a resultados inesperados.
Veamos un ejemplo sencillo para ilustrarlo. Supongamos que eres un profesor y quieres crear una función para registrar las calificaciones de los estudiantes en diferentes tareas. Deseas usar una lista para llevar un seguimiento de estas calificaciones. Entonces, podrías escribir inicialmente algo como esto:
def record_score(new_score, score_list=[]):
score_list.append(new_score)
return score_list
print(record_score(95)) # [95]
print(record_score(85)) # [95, 85]
Esto parece que funcionaría bien. La función
record_score
recibe una calificación y una lista. Si no se proporciona una lista, utiliza una vacía (el score_list=[]
en la definición de la función).Cuando llamas a
record_score(95)
, parece que la función funciona correctamente: devuelve una lista con una calificación de 95. Pero cuando luego llamas a record_score(85)
, la función devuelve una lista con 95 y 85. Es como si la función recordara la lista de la última vez que fue llamada.Esto podría hacer que te rasques la cabeza. En la mayoría de los lenguajes de programación, cuando una función termina, todas sus variables locales (como
score_list
) se descartan y se olvidan. La próxima vez que se llama a la función, comienza de nuevo.Pero en Python, el comportamiento es diferente cuando un objeto mutable se utiliza como argumento por defecto. Python solo crea el argumento por defecto una vez, cuando se define la función. Así que cada vez que llamas a
record_score
sin proporcionar una lista, Python utiliza la misma lista que creó la primera vez que se definió la función. Si modificas esa lista (agregando una calificación), la modificación permanece la siguiente vez que se llama a la función.Entonces, en nuestra función, el
score_list
se comparte entre todas las llamadas a la función donde no proporcionamos nuestra propia lista. Es por eso que las calificaciones se están acumulando en nuestro ejemplo.¡Esto no es lo que queríamos! Queríamos que cada llamada a
record_score
comenzara con una lista vacía, a menos que proporcionáramos nuestra propia lista.Para evitar esto, una práctica común es usar
None
como valor por defecto para argumentos que podrían ser mutables. Luego, dentro de la función, puedes crear un nuevo objeto mutable si el argumento es None
. Así es como podrías escribir la función record_score
para evitar el problema:def record_score(new_score, score_list=None):
if score_list is None: # Si no se proporcionó una lista,
score_list = [] # crea una nueva
score_list.append(new_score)
return score_list
print(record_score(95)) # [95]
print(record_score(85)) # [85]
En esta versión corregida, cada llamada a
record_score
que no proporciona su propia lista obtiene una lista nueva. De esta manera, las calificaciones no se comparten entre las llamadas a la función.💡
Entonces, la conclusión clave es esta:
Los argumentos mutables por defecto en Python pueden llevar a comportamientos inesperados porque se crean solo una vez, y el mismo objeto se utiliza cada vez que se llama a la función. Esto podría no ser lo que deseas, especialmente cuando modificas el objeto mutable. Para evitar este problema, puedes usar
None
como valor por defecto y crear un nuevo objeto en la función si es necesario.