El "menor asombro" y el argumento predeterminado mutable

Cualquiera que esté jugando con Python el tiempo suficiente ha sido mordido (o hecho pedazos) por el siguiente problema:

def foo(a=[]):
    a.append(5)
    return a

Los principiantes de Python esperarían que esta función siempre devuelva una lista con un solo elemento: [5]. En cambio, el resultado es 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 la llamó "un defecto de diseño dramático" del lenguaje. Le respondí que el comportamiento tenía una explicación subyacente, y de hecho es muy desconcertante e inesperado si no comprende los aspectos internos. 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 errores?)

Editar:

Baczek dio 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 colocar 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 se define, algo que presentaría un defecto profundo: el def línea sería "híbrido" en el sentido de que parte del enlace (del objeto de función) ocurriría en la definición, y parte (asignación de parámetros predeterminados) en el momento de la 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.

preguntado el 15 de julio de 09 a las 15:07

No tengo ninguna duda de que los argumentos cambiantes violan el principio del más mínimo asombro para una persona promedio, y he visto a principiantes que se acercan y luego reemplazan heroicamente las listas de correo con tuplas de correo. Sin embargo, los argumentos mutables todavía están en línea con Python Zen (Pep 20) y entran en la cláusula "obvio para holandés" (entendido / explotado por programadores de Python de núcleo duro). La solución alternativa recomendada con la cadena de documentos es la mejor, pero la resistencia a las cadenas de documentos y cualquier documento (escrito) no es tan infrecuente hoy en día. Personalmente, preferiría un decorador (digamos @fixed_defaults). -

Mi argumento cuando me encuentro con esto es: "¿Por qué necesitas crear una función que devuelva un mutable que, opcionalmente, podría ser un mutable que pasarías a la función? O modifica un mutable o crea uno nuevo. ¿Por qué necesitas hacer ambas cosas con una función? ¿Y por qué debería reescribirse el intérprete para permitirle hacer eso sin agregar tres líneas a su código? " Porque estamos hablando de reescribir la forma en que el intérprete maneja las definiciones de funciones y las evocaciones aquí. Eso es mucho por hacer para un caso de uso apenas necesario. -

"Los principiantes de Python esperarían que esta función siempre devuelva una lista con un solo elemento: [5]. "Soy un novato en Python, y no esperaría esto, porque obviamente foo([1]) regresará [1, 5]no, [5]. Lo que quisiste decir es que un novato esperaría la función llamado sin parámetro siempre volverá [5]. -

Esta pregunta pregunta "¿Por qué se implementó esto [de la manera incorrecta]?" No pregunta "¿Cuál es la forma correcta?", que está cubierto por [¿Por qué el uso de arg = None soluciona el problema del argumento predeterminado mutable de Python?] * (stackoverflow.com/questions/10676729/…). Los nuevos usuarios casi siempre están menos interesados ​​en lo primero y mucho más en lo segundo, por lo que a veces es un enlace / engaño muy útil para citar. -

0 Respuestas

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.