Cualquiera que haya jugado con Python el tiempo suficiente ha sido mordido (o despedazado) por el siguiente problema:
def foo(a=[]):
a.append(5)
return a
Principiantes de Python que se esperan esta función para volver siempre una lista con un solo elemento: [5]
. El resultado es, en cambio, muy diferente y muy sorprendente (para un novato):
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
Un gerente mío tuvo una vez su primer encuentro con esta característica, y lo calificó como "una falla dramática en el diseño" del lenguaje. Respondí que el comportamiento tenía una explicación subyacente, y de hecho es muy desconcertante e inesperado si no entiendes lo interno. Sin embargo, no pude responder (a mí mismo) la siguiente pregunta: ¿cuál es la razón para vincular el argumento predeterminado en la definición de la función y no en la ejecución de la función? Dudo que el comportamiento experimentado tenga un uso práctico (¿quién realmente usó variables estáticas en C, sin criar insectos?)
Editar :
Baczek hizo un ejemplo interesante. Junto con la mayoría de sus comentarios y los de Utaal en particular, elaboré más:
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
Para mí, parece que la decisión de diseño fue relativa a dónde poner el alcance de los parámetros: dentro de la función o "junto" con ella?
Hacer el enlace dentro de la función significaría que x
está efectivamente vinculado al valor predeterminado especificado cuando se llama a la función, no está definida, algo que presentaría un defecto profundo: la def
línea sería "híbrida" en el sentido de que parte del enlace (de el objeto de función) sucedería en la definición, y parte (asignación de parámetros predeterminados) en el momento de invocación de la función.
El comportamiento real es más consistente: todo lo de esa línea se evalúa cuando se ejecuta esa línea, es decir, en la definición de la función.
[5]
". Soy un principiante de Python, y yo no esperaría que esto, porque, obviamente, foo([1])
va a volver [1, 5]
, no [5]
. Lo que querías decir es que un novato esperaría que la función llamada sin parámetro siempre regrese [5]
.