“El menor asombro” y el argumento predeterminado mutable

Cualquier problema con Python durante 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 esperan que esta función siempre devuelva 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 su primer encuentro con esta característica y lo llamó “un defecto de diseño dramático” 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 errores?)

Editar :

Baczek hizo un ejemplo interesante. Junto con la mayoría de sus comentarios y los de Utaal en particular, elaboré más detalladamente:

     >>> 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 scope 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 la función se llama, no se define, algo que presentaría un defecto profundo: la línea de def sería “híbrida” 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 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.

    En realidad, esto no es un defecto de diseño, y no es por razones internas o de rendimiento.
    Viene simplemente del hecho de que las funciones en Python son objetos de primera clase, y no solo una pieza de código.

    Tan pronto como se llega a pensar de esta manera, entonces tiene sentido por completo: una función es un objeto que se evalúa en su definición; los parámetros predeterminados son una especie de “datos de miembro” y, por lo tanto, su estado puede cambiar de una llamada a otra, exactamente como en cualquier otro objeto.

    En cualquier caso, Effbot tiene una muy buena explicación de las razones de este comportamiento en Valores de parámetros predeterminados en Python .
    Lo encontré muy claro, y realmente sugiero leerlo para tener un mejor conocimiento de cómo funcionan los objetos funcionales.

    Supongamos que tienes el siguiente código

     fruits = ("apples", "bananas", "loganberries") def eat(food=fruits): ... 

    Cuando veo la statement de comer, lo menos sorprendente es pensar que si no se da el primer parámetro, será igual a la tupla ("apples", "bananas", "loganberries")

    Sin embargo, supuestamente más adelante en el código, hago algo como

     def some_random_function(): global fruits fruits = ("blueberries", "mangos") 

    luego, si los parámetros predeterminados estuvieran vinculados a la ejecución de la función en lugar de a la statement de la función, me sorprendería (de una manera muy mala) descubrir que las frutas habían sido cambiadas. Esto sería una IMO más sorprendente que descubrir que su función foo anterior estaba mutando la lista.

    El problema real reside en las variables mutables, y todos los idiomas tienen este problema en cierta medida. Aquí hay una pregunta: supongamos que en Java tengo el siguiente código:

     StringBuffer s = new StringBuffer("Hello World!"); Map counts = new HashMap(); counts.put(s, 5); s.append("!!!!"); System.out.println( counts.get(s) ); // does this work? 

    Ahora, ¿mi mapa utiliza el valor de la clave StringBuffer cuando se colocó en el mapa, o almacena la clave por referencia? De cualquier manera, alguien está asombrado; ya sea la persona que intentó sacar el objeto del Map usando un valor idéntico al que lo pusieron, o la persona que parece que no puede recuperar su objeto a pesar de que la clave que usan es literalmente la misma Objeto que se usó para ponerlo en el mapa (esta es la razón por la que Python no permite que sus tipos de datos integrados mutables se usen como claves de diccionario).

    Su ejemplo es un buen ejemplo de un caso en el que los recién llegados a Python serán sorprendidos y mordidos. Pero argumentaría que si “arreglamos” esto, entonces eso solo crearía una situación diferente donde serían mordidos en su lugar, y esa sería incluso menos intuitiva. Además, este es siempre el caso cuando se trata de variables mutables; siempre te encuentras con casos en los que alguien podría esperar intuitivamente uno o el comportamiento opuesto dependiendo del código que estén escribiendo.

    Personalmente, me gusta el enfoque actual de Python: los argumentos de la función predeterminada se evalúan cuando se define la función y ese objeto es siempre el predeterminado. Supongo que podrían usar un caso especial utilizando una lista vacía, pero ese tipo de carcasa especial causaría aún más asombro, por no mencionar que es incompatible con los anteriores.

    AFAICS nadie ha publicado todavía la parte relevante de la documentación :

    Los valores predeterminados de los parámetros se evalúan cuando se ejecuta la definición de la función. Esto significa que la expresión se evalúa una vez, cuando se define la función, y que se utiliza el mismo valor “precalculado” para cada llamada. Esto es especialmente importante de entender cuando un parámetro predeterminado es un objeto mutable, como una lista o un diccionario: si la función modifica el objeto (por ejemplo, agregando un elemento a una lista), el valor predeterminado se modifica en efecto. Esto generalmente no es lo que se pretendía. Una forma de evitar esto es utilizar Ninguno como predeterminado, y probarlo explícitamente en el cuerpo de la función […]

    No sé nada sobre el funcionamiento interno del intérprete de Python (y tampoco soy un experto en comstackdores e intérpretes), así que no me culpes si propongo algo que no sea comprensible o imposible.

    Siempre que los objetos de Python sean mutables , creo que esto debería tenerse en cuenta al diseñar los argumentos predeterminados. Cuando creas una instancia de una lista:

     a = [] 

    espera obtener una nueva lista a la que hace referencia un .

    ¿Por qué debería a = [] en

     def x(a=[]): 

    ¿crear una nueva lista en la definición de la función y no en la invocación? Es como si estuvieras preguntando “si el usuario no proporciona el argumento, crea una nueva lista y úsala como si hubiera sido generada por la persona que llama”. Creo que esto es ambiguo en su lugar:

     def x(a=datetime.datetime.now()): 

    usuario, ¿desea que se establezca de forma predeterminada la fecha y hora correspondiente a cuando está definiendo o ejecutando x ? En este caso, como en el anterior, mantendré el mismo comportamiento que si el argumento predeterminado “asignación” fuera la primera instrucción de la función (datetime.now () llamada en la invocación de la función). Por otro lado, si el usuario deseara la asignación en tiempo de definición, podría escribir:

     b = datetime.datetime.now() def x(a=b): 

    Lo sé, lo sé: eso es un cierre. Como alternativa, Python podría proporcionar una palabra clave para forzar el enlace en el tiempo de definición:

     def x(static a=b): 

    Bueno, la razón es simplemente que los enlaces se realizan cuando se ejecuta el código, y la definición de la función se ejecuta, bueno … cuando se definen las funciones.

    Compara esto:

     class BananaBunch: bananas = [] def addBanana(self, banana): self.bananas.append(banana) 

    Este código sufre de la misma casualidad inesperada. bananas es un atributo de clase, y por lo tanto, cuando le agregas cosas, se agrega a todas las instancias de esa clase. La razón es exactamente la misma.

    Es solo “Cómo funciona”, y hacer que funcione de manera diferente en el caso de la función probablemente sea complicado, y en el caso de la clase probablemente sea imposible, o al menos ralentizar mucho la creación de instancias de objetos, ya que tendría que mantener el código de clase alrededor y ejecutarlo cuando se crean objetos.

    Sí, es inesperado. Pero una vez que el centavo cae, encaja perfectamente con el funcionamiento de Python en general. De hecho, es una buena ayuda para la enseñanza, y una vez que entiendas por qué sucede esto, desarrollarás Python mucho mejor.

    Dicho esto, debería tener un lugar destacado en cualquier buen tutorial de Python. Porque como mencionas, todo el mundo se encuentra con este problema, tarde o temprano.

    Solía ​​pensar que crear los objetos en tiempo de ejecución sería el mejor enfoque. Ahora no estoy tan seguro, ya que pierdes algunas funciones útiles, aunque puede valer la pena independientemente, simplemente para evitar la confusión de los principiantes. Las desventajas de hacerlo son:

    1. Rendimiento

     def foo(arg=something_expensive_to_compute())): ... 

    Si se usa la evaluación de tiempo de llamada, entonces se llama a la función costosa cada vez que se usa su función sin un argumento. O pagaría un alto precio por cada llamada, o tendría que almacenar el valor manualmente en forma externa, contaminando su espacio de nombres y agregando verbosidad.

    2. Forzando los parámetros enlazados

    Un truco útil es vincular los parámetros de un lambda al enlace actual de una variable cuando se crea el lambda. Por ejemplo:

     funcs = [ lambda i=i: i for i in range(10)] 

    Esto devuelve una lista de funciones que devuelven 0,1,2,3 … respectivamente. Si se modifica el comportamiento, en su lugar, vincularán i al valor de tiempo de llamada de i, por lo que obtendría una lista de funciones que todas devolvieron 9 .

    La única forma de implementar esto sería crear un cierre adicional con el enlace i, es decir:

     def make_func(i): return lambda: i funcs = [make_func(i) for i in range(10)] 

    3. Introspección

    Considere el código:

     def foo(a='test', b=100, c=[]): print a,b,c 

    Podemos obtener información sobre los argumentos y los valores predeterminados utilizando el módulo de inspect , que

     >>> inspect.getargspec(foo) (['a', 'b', 'c'], None, None, ('test', 100, [])) 

    Esta información es muy útil para cosas como la generación de documentos, metaprogtwigción, decoradores, etc.

    Ahora, suponga que el comportamiento de los valores predeterminados podría cambiarse de modo que este sea el equivalente de:

     _undefined = object() # sentinel value def foo(a=_undefined, b=_undefined, c=_undefined) if a is _undefined: a='test' if b is _undefined: b=100 if c is _undefined: c=[] 

    Sin embargo, hemos perdido la capacidad de realizar una introspección y ver cuáles son los argumentos predeterminados. Debido a que los objetos no se han construido, nunca podemos obtenerlos sin llamar a la función. Lo mejor que podemos hacer es almacenar el código fuente y devolverlo como una cadena.

    5 puntos en defensa de Python.

    1. Simplicidad : el comportamiento es simple en el siguiente sentido: la mayoría de las personas caen en esta trampa solo una vez, no varias veces.

    2. Consistencia : Python siempre pasa objetos, no nombres. El parámetro predeterminado es, obviamente, parte del encabezado de la función (no el cuerpo de la función). Por lo tanto, debe evaluarse en el tiempo de carga del módulo (y solo en el tiempo de carga del módulo, a menos que esté nested), no en el tiempo de llamada de función.

    3. Utilidad : como señala Frederik Lundh en su explicación de “Valores de parámetros predeterminados en Python” , el comportamiento actual puede ser bastante útil para la progtwigción avanzada. (Utilizar con moderación.)

    4. Documentación suficiente : en la documentación más básica de Python, el tutorial, el problema se anuncia en voz alta como una “Advertencia importante” en la primera subsección de la Sección “Más sobre funciones definidas” . La advertencia incluso usa negrita, que rara vez se aplica fuera de los encabezados. RTFM: Lea el fino manual.

    5. Meta-aprendizaje : caer en la trampa es en realidad un momento muy útil (al menos si eres un aprendiz reflexivo), porque posteriormente entenderás mejor el punto “Consistencia” que aparece arriba y eso te enseñará mucho sobre Python.

    ¿Por qué no haces una introspección?

    Estoy realmente sorprendido de que nadie haya realizado la introspección perspicaz ofrecida por Python ( 2 y 3 ) en callables.

    Dada una pequeña función de func definida como:

     >>> def func(a = []): ... a.append(5) 

    Cuando Python lo encuentra, lo primero que hará es comstackrlo para crear un objeto de code para esta función. Mientras se realiza este paso de comstackción, Python evalúa * y luego almacena los argumentos predeterminados (una lista vacía [] aquí) en el objeto de función en sí . Como mencionó la respuesta principal: la lista a ahora puede considerarse un miembro de la función func .

    Entonces, hagamos una introspección, un antes y un después para examinar cómo se expande la lista dentro del objeto de función. Estoy usando Python 3.x para esto, para Python 2 se aplica lo mismo (use __defaults__ o func_defaults en Python 2; sí, dos nombres para la misma cosa).

    Función antes de la ejecución:

     >>> def func(a = []): ... a.append(5) ... 

    Después de que Python ejecute esta definición, tomará todos los parámetros predeterminados especificados ( a = [] aquí) y los __defaults__ en el atributo __defaults__ para el objeto de función (sección relevante: Callables):

     >>> func.__defaults__ ([],) 

    Ok, entonces una lista vacía como entrada única en __defaults__ , tal como se esperaba.

    Función después de la ejecución:

    Ahora ejecutemos esta función:

     >>> func() 

    Ahora, veamos esos __defaults__ nuevo:

     >>> func.__defaults__ ([5],) 

    ¿Asombrado? ¡El valor dentro del objeto cambia! Las llamadas consecutivas a la función ahora simplemente se agregarán a ese objeto de list incrustada:

     >>> func(); func(); func() >>> func.__defaults__ ([5, 5, 5, 5],) 

    Entonces, ahí lo tienen, la razón por la que ocurre este ‘defecto’ , es porque los argumentos predeterminados son parte del objeto de función. No hay nada raro aquí, todo es un poco sorprendente.

    La solución común para combatir esto es usar None como predeterminado y luego inicializar en el cuerpo de la función:

     def func(a = None): # or: a = [] if a is None else a if a is None: a = [] 

    Dado que el cuerpo de la función se ejecuta de nuevo cada vez, siempre se obtiene una nueva lista vacía si no se pasa ningún argumento para a .


    Para verificar más a fondo que la lista en __defaults__ es la misma que la que se usa en la función function, puede cambiar su función para devolver la id de la lista a usarla dentro del cuerpo de la función. Luego, compárelo con la lista en __defaults__ (posición [0] en __defaults__ ) y verá cómo se están refiriendo a la misma instancia de lista:

     >>> def func(a = []): ... a.append(5) ... return id(a) >>> >>> id(func.__defaults__[0]) == func() True 

    ¡Todo con el poder de la introspección!


    * Para verificar que Python evalúa los argumentos predeterminados durante la comstackción de la función, intente ejecutar lo siguiente:

     def bar(a=input('Did you just see me without calling the function?')): pass # use raw_input in Py2 

    Como se dará cuenta, se llama a input() antes del proceso de comstackción de la función y se vincula a la bar nombres.

    Este comportamiento es fácil de explicar por:

    1. La statement de función (clase, etc.) se ejecuta solo una vez, creando todos los objetos de valor predeterminados
    2. todo se pasa por referencia

    Asi que:

     def x(a=0, b=[], c=[], d=0): a = a + 1 b = b + [1] c.append(1) print a, b, c 
    1. a no cambia – cada llamada de asignación crea un nuevo objeto int – el nuevo objeto se imprime
    2. b no cambia: la nueva matriz se crea a partir del valor predeterminado y se imprime
    3. c cambios: la operación se realiza en el mismo objeto y se imprime

    Lo que estás preguntando es por qué esto:

     def func(a=[], b = 2): pass 

    no es internamente equivalente a esto:

     def func(a=None, b = None): a_default = lambda: [] b_default = lambda: 2 def actual_func(a=None, b=None): if a is None: a = a_default() if b is None: b = b_default() return actual_func func = func() 

    excepto en el caso de llamar explícitamente a func (None, None), que ignoraremos.

    En otras palabras, en lugar de evaluar los parámetros predeterminados, ¿por qué no almacenar cada uno de ellos y evaluarlos cuando se llama a la función?

    Probablemente haya una respuesta allí mismo: efectivamente convertiría cada función con parámetros predeterminados en un cierre. Incluso si todo está oculto en el intérprete y no es un cierre completo, los datos deben almacenarse en algún lugar. Sería más lento y usaría más memoria.

    1) The so-called problem of “Mutable Default Argument” is in general a special example demonstrating that:
    “All functions with this problem suffer also from similar side effect problem on the actual parameter ,”
    That is against the rules of functional programming, usually undesiderable and should be fixed both together.

    Ejemplo:

     def foo(a=[]): # the same problematic function a.append(5) return a >>> somevar = [1, 2] # an example without a default parameter >>> foo(somevar) [1, 2, 5] >>> somevar [1, 2, 5] # usually expected [1, 2] 

    Solution : a copy
    An absolutely safe solution is to copy or deepcopy the input object first and then to do whatever with the copy.

     def foo(a=[]): a = a[:] # a copy a.append(5) return a # or everything safe by one line: "return a + [5]" 

    Many builtin mutable types have a copy method like some_dict.copy() or some_set.copy() or can be copied easy like somelist[:] or list(some_list) . Every object can be also copied by copy.copy(any_object) or more thorough by copy.deepcopy() (the latter useful if the mutable object is composed from mutable objects). Some objects are fundamentally based on side effects like “file” object and can not be meaningfully reproduced by copy. copying

    Example problem for a similar SO question

     class Test(object): # the original problematic class def __init__(self, var1=[]): self._var1 = var1 somevar = [1, 2] # an example without a default parameter t1 = Test(somevar) t2 = Test(somevar) t1._var1.append([1]) print somevar # [1, 2, [1]] but usually expected [1, 2] print t2._var1 # [1, 2, [1]] but usually expected [1, 2] 

    It shouldn’t be neither saved in any public attribute of an instance returned by this function. (Assuming that private attributes of instance should not be modified from outside of this class or subclasses by convention. ie _var1 is a private attribute )

    Conclusión:
    Input parameters objects shouldn’t be modified in place (mutated) nor they should not be binded into an object returned by the function. (If we prefere programming without side effects which is strongly recommended. see Wiki about “side effect” (The first two paragraphs are relevent in this context.) .)

    2)
    Only if the side effect on the actual parameter is required but unwanted on the default parameter then the useful solution is def ...(var1=None): if var1 is None: var1 = [] More..

    3) In some cases is the mutable behavior of default parameters useful .

    This actually has nothing to do with default values, other than that it often comes up as an unexpected behaviour when you write functions with mutable default values.

     >>> def foo(a): a.append(5) print a >>> a = [5] >>> foo(a) [5, 5] >>> foo(a) [5, 5, 5] >>> foo(a) [5, 5, 5, 5] >>> foo(a) [5, 5, 5, 5, 5] 

    No default values in sight in this code, but you get exactly the same problem.

    The problem is that foo is modifying a mutable variable passed in from the caller, when the caller doesn’t expect this. Code like this would be fine if the function was called something like append_5 ; then the caller would be calling the function in order to modify the value they pass in, and the behaviour would be expected. But such a function would be very unlikely to take a default argument, and probably wouldn’t return the list (since the caller already has a reference to that list; the one it just passed in).

    Your original foo , with a default argument, shouldn’t be modifying a whether it was explicitly passed in or got the default value. Your code should leave mutable arguments alone unless it is clear from the context/name/documentation that the arguments are supposed to be modified. Using mutable values passed in as arguments as local temporaries is an extremely bad idea, whether we’re in Python or not and whether there are default arguments involved or not.

    If you need to destructively manipulate a local temporary in the course of computing something, and you need to start your manipulation from an argument value, you need to make a copy.

    It’s a performance optimization. As a result of this functionality, which of these two function calls do you think is faster?

     def print_tuple(some_tuple=(1,2,3)): print some_tuple print_tuple() #1 print_tuple((1,2,3)) #2 

    Te daré una pista. Here’s the disassembly (see http://docs.python.org/library/dis.html ):

    # 1

     0 LOAD_GLOBAL 0 (print_tuple) 3 CALL_FUNCTION 0 6 POP_TOP 7 LOAD_CONST 0 (None) 10 RETURN_VALUE 

    # 2

      0 LOAD_GLOBAL 0 (print_tuple) 3 LOAD_CONST 4 ((1, 2, 3)) 6 CALL_FUNCTION 1 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE 

    I doubt the experienced behavior has a practical use (who really used static variables in C, without breeding bugs ?)

    As you can see, there is a performance benefit when using immutable default arguments. This can make a difference if it’s a frequently called function or the default argument takes a long time to construct. Also, bear in mind that Python isn’t C. In C you have constants that are pretty much free. In Python you don’t have this benefit.

    Already busy topic, but from what I read here, the following helped me realizing how it’s working internally:

     def bar(a=[]): print id(a) a = a + [1] print id(a) return a >>> bar() 4484370232 4484524224 [1] >>> bar() 4484370232 4484524152 [1] >>> bar() 4484370232 # Never change, this is 'class property' of the function 4484523720 # Always a new object [1] >>> id(bar.func_defaults[0]) 4484370232 

    The shortest answer would probably be “definition is execution”, therefore the whole argument makes no strict sense. As a more contrived example, you may cite this:

     def a(): return [] def b(x=a()): print x 

    Hopefully it’s enough to show that not executing the default argument expressions at the execution time of the def statement isn’t easy or doesn’t make sense, or both.

    I agree it’s a gotcha when you try to use default constructors, though.

    Python: The Mutable Default Argument

    Default arguments get evaluated at the time the function is compiled into a function object. When used by the function, multiple times by that function, they are and remain the same object.

    When they are mutable, when mutated (for example, by adding an element to it) they remain mutated on consecutive calls.

    They stay mutated because they are the same object each time.

    Equivalent code:

    Since the list is bound to the function when the function object is compiled and instantiated, this:

     def foo(mutable_default_argument=[]): # make a list the default argument """function that uses a list""" 

    is almost exactly equivalent to this:

     _a_list = [] # create a list in the globals def foo(mutable_default_argument=_a_list): # make it the default argument """function that uses a list""" del _a_list # remove globals name binding 

    Demostración

    Here’s a demonstration – you can verify that they are the same object each time they are referenced by

    • seeing that the list is created before the function has finished compiling to a function object,
    • observing that the id is the same each time the list is referenced,
    • observing that the list stays changed when the function that uses it is called a second time,
    • observing the order in which the output is printed from the source (which I conveniently numbered for you):

    example.py

     print('1. Global scope being evaluated') def create_list(): '''noisily create a list for usage as a kwarg''' l = [] print('3. list being created and returned, id: ' + str(id(l))) return l print('2. example_function about to be compiled to an object') def example_function(default_kwarg1=create_list()): print('appending "a" in default default_kwarg1') default_kwarg1.append("a") print('list with id: ' + str(id(default_kwarg1)) + ' - is now: ' + repr(default_kwarg1)) print('4. example_function compiled: ' + repr(example_function)) if __name__ == '__main__': print('5. calling example_function twice!:') example_function() example_function() 

    and running it with python example.py :

     1. Global scope being evaluated 2. example_function about to be compiled to an object 3. list being created and returned, id: 140502758808032 4. example_function compiled:  5. calling example_function twice!: appending "a" in default default_kwarg1 list with id: 140502758808032 - is now: ['a'] appending "a" in default default_kwarg1 list with id: 140502758808032 - is now: ['a', 'a'] 

    Does this violate the principle of “Least Astonishment”?

    This order of execution is frequently confusing to new users of Python. If you understand the Python execution model, then it becomes quite expected.

    The usual instruction to new Python users:

    But this is why the usual instruction to new users is to create their default arguments like this instead:

     def example_function_2(default_kwarg=None): if default_kwarg is None: default_kwarg = [] 

    This uses the None singleton as a sentinel object to tell the function whether or not we’ve gotten an argument other than the default. If we get no argument, then we actually want to use a new empty list, [] , as the default.

    As the tutorial section on control flow says:

    If you don’t want the default to be shared between subsequent calls, you can write the function like this instead:

     def f(a, L=None): if L is None: L = [] L.append(a) return L 

    A simple workaround using None

     >>> def bar(b, data=None): ... data = data or [] ... data.append(b) ... return data ... >>> bar(3) [3] >>> bar(3) [3] >>> bar(3) [3] >>> bar(3, [34]) [34, 3] >>> bar(3, [34]) [34, 3] 

    This behavior is not surprising if you take the following into consideration:

    1. The behavior of read-only class attributes upon assignment attempts, and that
    2. Functions are objects (explained well in the accepted answer).

    The role of (2) has been covered extensively in this thread. (1) is likely the astonishment causing factor, as this behavior is not “intuitive” when coming from other languages.

    (1) is described in the Python tutorial on classes . In an attempt to assign a value to a read-only class attribute:

    …all variables found outside of the innermost scope are read-only ( an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged ).

    Look back to the original example and consider the above points:

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

    Here foo is an object and a is an attribute of foo (available at foo.func_defs[0] ). Since a is a list, a is mutable and is thus a read-write attribute of foo . It is initialized to the empty list as specified by the signature when the function is instantiated, and is available for reading and writing as long as the function object exists.

    Calling foo without overriding a default uses that default’s value from foo.func_defs . In this case, foo.func_defs[0] is used for a within function object’s code scope. Changes to a change foo.func_defs[0] , which is part of the foo object and persists between execution of the code in foo .

    Now, compare this to the example from the documentation on emulating the default argument behavior of other languages , such that the function signature defaults are used every time the function is executed:

     def foo(a, L=None): if L is None: L = [] L.append(a) return L 

    Taking (1) and (2) into account, one can see why this accomplishes the the desired behavior:

    • When the foo function object is instantiated, foo.func_defs[0] is set to None , an immutable object.
    • When the function is executed with defaults (with no parameter specified for L in the function call), foo.func_defs[0] ( None ) is available in the local scope as L .
    • Upon L = [] , the assignment cannot succeed at foo.func_defs[0] , because that attribute is read-only.
    • Per (1) , a new local variable also named L is created in the local scope and used for the remainder of the function call. foo.func_defs[0] thus remains unchanged for future invocations of foo .

    The solutions here are:

    1. Use None as your default value (or a nonce object ), and switch on that to create your values at runtime; o
    2. Use a lambda as your default parameter, and call it within a try block to get the default value (this is the sort of thing that lambda abstraction is for).

    The second option is nice because users of the function can pass in a callable, which may be already existing (such as a type )

    I am going to demonstrate an alternative structure to pass a default list value to a function (it works equally well with dictionaries).

    As others have extensively commented, the list parameter is bound to the function when it is defined as opposed to when it is executed. Because lists and dictionaries are mutable, any alteration to this parameter will affect other calls to this function. As a result, subsequent calls to the function will receive this shared list which may have been altered by any other calls to the function. Worse yet, two parameters are using this function’s shared parameter at the same time oblivious to the changes made by the other.

    Wrong Method (probably…) :

     def foo(list_arg=[5]): return list_arg a = foo() a.append(6) >>> a [5, 6] b = foo() b.append(7) # The value of 6 appended to variable 'a' is now part of the list held by 'b'. >>> b [5, 6, 7] # Although 'a' is expecting to receive 6 (the last element it appended to the list), # it actually receives the last element appended to the shared list. # It thus receives the value 7 previously appended by 'b'. >>> a.pop() 7 

    You can verify that they are one and the same object by using id :

     >>> id(a) 5347866528 >>> id(b) 5347866528 

    Per Brett Slatkin’s “Effective Python: 59 Specific Ways to Write Better Python”, Item 20: Use None and Docstrings to specify dynamic default arguments (p. 48)

    The convention for achieving the desired result in Python is to provide a default value of None and to document the actual behaviour in the docstring.

    This implementation ensures that each call to the function either receives the default list or else the list passed to the function.

    Preferred Method :

     def foo(list_arg=None): """ :param list_arg: A list of input values. If none provided, used a list with a default value of 5. """ if not list_arg: list_arg = [5] return list_arg a = foo() a.append(6) >>> a [5, 6] b = foo() b.append(7) >>> b [5, 7] c = foo([10]) c.append(11) >>> c [10, 11] 

    There may be legitimate use cases for the ‘Wrong Method’ whereby the programmer intended the default list parameter to be shared, but this is more likely the exception than the rule.

    I sometimes exploit this behavior as an alternative to the following pattern:

     singleton = None def use_singleton(): global singleton if singleton is None: singleton = _make_singleton() return singleton.use_me() 

    If singleton is only used by use_singleton , I like the following pattern as a replacement:

     # _make_singleton() is called only once when the def is executed def use_singleton(singleton=_make_singleton()): return singleton.use_me() 

    I’ve used this for instantiating client classes that access external resources, and also for creating dicts or lists for memoization.

    Since I don’t think this pattern is well known, I do put a short comment in to guard against future misunderstandings.

    You can get round this by replacing the object (and therefore the tie with the scope):

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

    Ugly, but it works.

    When we do this:

     def foo(a=[]): ... 

    … we assign the argument a to an unnamed list, if the caller does not pass the value of a.

    To make things simpler for this discussion, let’s temporarily give the unnamed list a name. How about pavlo ?

     def foo(a=pavlo): ... 

    At any time, if the caller doesn’t tell us what a is, we reuse pavlo .

    If pavlo is mutable (modifiable), and foo ends up modifying it, an effect we notice the next time foo is called without specifying a .

    So this is what you see (Remember, pavlo is initialized to []):

      >>> foo() [5] 

    Now, pavlo is [5].

    Calling foo() again modifies pavlo again:

     >>> foo() [5, 5] 

    Specifying a when calling foo() ensures pavlo is not touched.

     >>> ivan = [1, 2, 3, 4] >>> foo(a=ivan) [1, 2, 3, 4, 5] >>> ivan [1, 2, 3, 4, 5] 

    So, pavlo is still [5, 5] .

     >>> foo() [5, 5, 5] 

    It may be true that:

    1. Someone is using every language/library feature, and
    2. Switching the behavior here would be ill-advised, but

    it is entirely consistent to hold to both of the features above and still make another point:

    1. It is a confusing feature and it is unfortunate in Python.

    The other answers, or at least some of them either make points 1 and 2 but not 3, or make point 3 and downplay points 1 and 2. But all three are true.

    It may be true that switching horses in midstream here would be asking for significant breakage, and that there could be more problems created by changing Python to intuitively handle Stefano’s opening snippet. And it may be true that someone who knew Python internals well could explain a minefield of consequences. Sin embargo,

    The existing behavior is not Pythonic, and Python is successful because very little about the language violates the principle of least astonishment anywhere near this badly. It is a real problem, whether or not it would be wise to uproot it. It is a design flaw. If you understand the language much better by trying to trace out the behavior, I can say that C++ does all of this and more; you learn a lot by navigating, for instance, subtle pointer errors. But this is not Pythonic: people who care about Python enough to persevere in the face of this behavior are people who are drawn to the language because Python has far fewer surprises than other language. Dabblers and the curious become Pythonistas when they are astonished at how little time it takes to get something working–not because of a design fl–I mean, hidden logic puzzle–that cuts against the intuitions of programmers who are drawn to Python because it Just Works .

    This “bug” gave me a lot of overtime work hours! But I’m beginning to see a potential use of it (but I would have liked it to be at the execution time, still)

    I’m gonna give you what I see as a useful example.

     def example(errors=[]): # statements # Something went wrong mistake = True if mistake: tryToFixIt(errors) # Didn't work.. let's try again tryToFixItAnotherway(errors) # This time it worked return errors def tryToFixIt(err): err.append('Attempt to fix it') def tryToFixItAnotherway(err): err.append('Attempt to fix it by another way') def main(): for item in range(2): errors = example() print '\n'.join(errors) main() 

    prints the following

     Attempt to fix it Attempt to fix it by another way Attempt to fix it Attempt to fix it by another way 

    I think the answer to this question lies in how python pass data to parameter (pass by value or by reference), not mutability or how python handle the “def” statement.

    A brief introduction. First, there are two type of data types in python, one is simple elementary data type, like numbers, and another data type is objects. Second, when passing data to parameters, python pass elementary data type by value, ie, make a local copy of the value to a local variable, but pass object by reference, ie, pointers to the object.

    Admitting the above two points, let’s explain what happened to the python code. It’s only because of passing by reference for objects, but has nothing to do with mutable/immutable, or arguably the fact that “def” statement is executed only once when it is defined.

    [] is an object, so python pass the reference of [] to a , ie, a is only a pointer to [] which lies in memory as an object. There is only one copy of [] with, however, many references to it. For the first foo(), the list [] is changed to 1 by append method. But Note that there is only one copy of the list object and this object now becomes 1 . When running the second foo(), what effbot webpage says (items is not evaluated any more) is wrong. a is evaluated to be the list object, although now the content of the object is 1 . This is the effect of passing by reference! The result of foo(3) can be easily derived in the same way.

    To further validate my answer, let’s take a look at two additional codes.

    ====== No. 2 ========

     def foo(x, items=None): if items is None: items = [] items.append(x) return items foo(1) #return [1] foo(2) #return [2] foo(3) #return [3] 

    [] is an object, so is None (the former is mutable while the latter is immutable. But the mutability has nothing to do with the question). None is somewhere in the space but we know it’s there and there is only one copy of None there. So every time foo is invoked, items is evaluated (as opposed to some answer that it is only evaluated once) to be None, to be clear, the reference (or the address) of None. Then in the foo, item is changed to [], ie, points to another object which has a different address.

    ====== No. 3 =======

     def foo(x, items=[]): items.append(x) return items foo(1) # returns [1] foo(2,[]) # returns [2] foo(3) # returns [1,3] 

    The invocation of foo(1) make items point to a list object [] with an address, say, 11111111. the content of the list is changed to 1 in the foo function in the sequel, but the address is not changed, still 11111111. Then foo(2,[]) is coming. Although the [] in foo(2,[]) has the same content as the default parameter [] when calling foo(1), their address are different! Since we provide the parameter explicitly, items has to take the address of this new [] , say 2222222, and return it after making some change. Now foo(3) is executed. since only x is provided, items has to take its default value again. What’s the default value? It is set when defining the foo function: the list object located in 11111111. So the items is evaluated to be the address 11111111 having an element 1. The list located at 2222222 also contains one element 2, but it is not pointed by items any more. Consequently, An append of 3 will make items [1,3].

    From the above explanations, we can see that the effbot webpage recommended in the accepted answer failed to give a relevant answer to this question. What is more, I think a point in the effbot webpage is wrong. I think the code regarding the UI.Button is correct:

     for i in range(10): def callback(): print "clicked button", i UI.Button("button %s" % i, callback) 

    Each button can hold a distinct callback function which will display different value of i . I can provide an example to show this:

     x=[] for i in range(10): def callback(): print(i) x.append(callback) 

    If we execute x[7]() we’ll get 7 as expected, and x[9]() will gives 9, another value of i .

    This is not a design flaw . Anyone who trips over this is doing something wrong.

    There are 3 cases I see where you might run into this problem:

    1. You intend to modify the argument as a side effect of the function. In this case it never makes sense to have a default argument. The only exception is when you’re abusing the argument list to have function attributes, eg cache={} , and you wouldn’t be expected to call the function with an actual argument at all.
    2. You intend to leave the argument unmodified, but you accidentally did modify it. That’s a bug, fix it.
    3. You intend to modify the argument for use inside the function, but didn’t expect the modification to be viewable outside of the function. In that case you need to make a copy of the argument, whether it was the default or not! Python is not a call-by-value language so it doesn’t make the copy for you, you need to be explicit about it.

    The example in the question could fall into category 1 or 3. It’s odd that it both modifies the passed list and returns it; you should pick one or the other.

    Just change the function to be:

     def notastonishinganymore(a = []): '''The name is just a joke :)''' a = a[:] a.append(5) return a 

    TLDR: Define-time defaults are consistent and strictly more expressive.


    Defining a function affects two scopes: the defining scope containing the function, and the execution scope contained by the function. While it is pretty clear how blocks map to scopes, the question is where def (): belongs to:

     ... # defining scope def name(parameter=default): # ??? ... # execution scope 

    The def name part must evaluate in the defining scope – we want name to be available there, after all. Evaluating the function only inside itself would make it inaccessible.

    Since parameter is a constant name, we can “evaluate” it at the same time as def name . This also has the advantage it produces the function with a known signature as name(parameter=...): , instead of a bare name(...): .

    Now, when to evaluate default ?

    Consistency already says “at definition”: everything else of def (): is best evaluated at definition as well. Delaying parts of it would be the astonishing choice.

    The two choices are not equivalent, either: If default is evaluated at definition time, it can still affect execution time. If default is evaluated at execution time, it cannot affect definition time. Choosing “at definition” allows expressing both cases, while choosing “at execution” can express only one:

     def name(parameter=defined): # set default at definition time ... def name(parameter=None): # delay default until execution time parameter = [] if parameter is None else parameter ... 
     >>> def a(): >>> print "a executed" >>> return [] >>> x =a() a executed >>> def b(m=[]): >>> m.append(5) >>> print m >>> b(x) [5] >>> b(x) [5, 5]