python function arguments mutable

¿Por qué el uso de None soluciona el problema del argumento predeterminado mutable de Python?



function arguments (4)

Parece que a_list todavía se inicializaría solo una vez

La "inicialización" no es algo que suceda con las variables en Python, porque las variables en Python son solo nombres. La "inicialización" solo sucede con los objetos, y se realiza a través del método '' __init__ clase.

Cuando escribes a = 0 , eso es una asignación. Es decir, " a se referirá al objeto descrito por la expresión 0 ". No es inicialización; a puede nombrar cualquier otra cosa de cualquier tipo en cualquier momento posterior, y eso sucede como resultado de asignar otra cosa a a . La asignación es sólo la asignación. El primero no es especial.

Cuando escribe def good_append(new_item, a_list=None) , eso no es "inicializar" a_list . Está configurando una referencia interna a un objeto, el resultado de evaluar None , de modo que cuando se llama a good_append sin un segundo parámetro, ese objeto se asigna automáticamente a a_list .

lo que significa que a_list solo se restablecería a [] en la primera invocación

No, a_list se establece en [] cada vez que a_list is None para empezar. Es decir, cuando se pasa None explícitamente, o se omite el argumento.

El problema con [] ocurre porque la expresión [] solo se evalúa una vez en este contexto. Cuando se compila la función, se evalúa [] , se crea un objeto de lista específico , que está vacío para comenzar, y ese objeto se usa como predeterminado.

¿Cómo se limpia good_append cada vez que se ejecuta good_append ?

No lo hace No necesita ser.

¿Sabes cómo se describe el problema con "argumentos predeterminados mutables"?

None no es mutable.

El problema se produce cuando modifica el objeto que el parámetro tiene por defecto.

a_list = [] no modifica ningún objeto a_list haya hecho referencia anteriormente. No puede; Los objetos arbitrarios no pueden transformarse mágicamente in situ en listas vacías. a_list = [] significa " a_list dejará de referirse a lo que se refirió anteriormente, y comenzará a referirse a [] ". El objeto referido previamente no ha cambiado.

Cuando se compila la función, y uno de los argumentos tiene un valor predeterminado, ese valor, un objeto , se integra en la función (que también es un objeto). Cuando se escribe un código que muta un objeto, el objeto muta. Si el objeto al que se hace referencia es el objeto incorporado a la función, todavía muta.

Pero no puedes mutar None . Es inmutable.

Puedes mutar [] . Es una lista, y las listas son mutables. Anexar un elemento a una lista anula la lista.

Estoy en el punto de aprender Python en el que estoy lidiando con el problema Argumento predeterminado mutable .

def bad_append(new_item, a_list=[]): a_list.append(new_item) return a_list def good_append(new_item, a_list=None): if a_list is None: a_list = [] a_list.append(new_item) return a_list

Entiendo que a_list se inicializa solo cuando se encuentra por primera vez la instrucción def , y es por eso que las llamadas subsiguientes de bad_append usan el mismo objeto de lista.

Lo que no entiendo es por qué good_append funciona de manera diferente. Parece que a_list aún se inicializaría solo una vez; por lo tanto, la instrucción if solo sería verdadera en la primera invocación de la función, lo que significa que a_list solo se restablecerá a [] en la primera invocación, lo que significa que aún acumulará todos los valores de new_item pasados ​​y seguirá teniendo errores.

¿Por qué no lo es? ¿Qué concepto me estoy perdiendo? ¿Cómo se limpia good_append cada vez que se ejecuta good_append ?


El problema solo existe si el valor predeterminado es mutable, mientras que None no lo es. Lo que se almacena junto con el objeto de función es el valor predeterminado. Cuando se llama a la función, el contexto de la función se inicializa con el valor predeterminado.

a_list = []

simplemente asigna un nuevo objeto al nombre a_list en el contexto de la llamada a la función actual. No modifica None de ninguna manera.


El valor predeterminado de a_list (o cualquier otro valor predeterminado, para esa materia) se almacena en los interiores de la función una vez que se ha inicializado y, por lo tanto, se puede modificar de cualquier manera:

>>> def f(x=[]): return x ... >>> f.func_defaults ([],) >>> f.func_defaults[0] is f()

Por lo tanto, el valor en func_defaults es el mismo que se conoce también como función interna (y se devuelve en mi ejemplo para acceder desde el exterior).

IOW, lo que sucede cuando se llama a f() es un implícito x = f.func_defaults[0] . Si ese objeto se modifica posteriormente, mantendrás esa modificación.

En contraste, una asignación dentro de la función obtiene siempre un nuevo [] . Cualquier modificación durará hasta que la última referencia a ese [] haya desaparecido; En la siguiente llamada de función, se crea un nuevo [] .

IOW de nuevo, no es cierto que [] obtenga el mismo objeto en cada ejecución, pero (en el caso del argumento predeterminado) solo se ejecuta una vez y luego se conserva.


No, en good_insert a_list no se inicializa solo una vez.

Cada vez que se llama a la función sin especificar el argumento a_list , se usa el valor predeterminado y se usa y devuelve una nueva instancia de la list , la nueva lista no reemplaza el valor predeterminado.