Python Language Definiendo una función con argumentos mutables opcionales.


Ejemplo

Existe un problema cuando se usan argumentos opcionales con un tipo predeterminado mutable (descrito en Definición de una función con argumentos opcionales ), lo que puede conducir a un comportamiento inesperado.

Explicación

Este problema surge porque los argumentos predeterminados de una función se inicializan una vez , en el momento en que se define la función y no (como en muchos otros idiomas) cuando se llama a la función. Los valores predeterminados se almacenan dentro de la variable miembro __defaults__ del objeto de __defaults__ .

def f(a, b=42, c=[]):
    pass

print(f.__defaults__)
# Out: (42, [])

Para los tipos inmutables (ver Pasar argumento y mutabilidad ) esto no es un problema porque no hay manera de mutar la variable; solo se puede reasignar, sin cambiar el valor original. Por lo tanto, se garantiza que los subsiguientes tengan el mismo valor predeterminado. Sin embargo, para un tipo mutable , el valor original puede mutar, haciendo llamadas a sus diversas funciones miembro. Por lo tanto, no se garantiza que las llamadas sucesivas a la función tengan el valor predeterminado inicial.

def append(elem, to=[]):
    to.append(elem)      # This call to append() mutates the default variable "to"
    return to

append(1)
# Out: [1]

append(2)  # Appends it to the internally stored list
# Out: [1, 2]

append(3, [])  # Using a new created list gives the expected result
# Out: [3]

# Calling it again without argument will append to the internally stored list again
append(4)   
# Out: [1, 2, 4]

Nota: Algunos IDE como PyCharm emitirán una advertencia cuando se especifique un tipo mutable como atributo predeterminado.

Solución

Si desea asegurarse de que el argumento predeterminado sea siempre el que especifique en la definición de la función, entonces la solución es usar siempre un tipo inmutable como su argumento predeterminado.

Un modismo común para lograr esto cuando se necesita un tipo mutable como predeterminado, es utilizar None (inmutable) como argumento predeterminado y luego asignar el valor predeterminado real a la variable de argumento si es igual a None .

def append(elem, to=None):
    if to is None:
        to = []

    to.append(elem)
    return to