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.
 
To check your solution you need to sign in
Sign in to continue