with examples def python scope parameters default-value function-calls

examples - python function parameters



¿Cuál es el alcance de un parámetro predeterminado en Python? (7)

Digamos que tienes el siguiente código:

def func(a=[]): a.append(1) print("A:", a) func() func() func()

Puedes usar la sangría de python para ayudarte a entender lo que está pasando. Todo lo que está al ras del margen izquierdo se ejecuta cuando se ejecuta el archivo. Todo lo que está sangrado se compila en un objeto de código que se ejecuta cuando se llama a func() . Por lo tanto, la función se define y sus argumentos predeterminados se establecen una vez, cuando el programa se ejecuta (porque la instrucción def está al ras de la izquierda).

Lo que hace con los argumentos por defecto es un tema interesante. En Python 3, coloca la mayor parte de la información sobre una función en dos lugares: func.__code__ y func.__defaults__ . En Python 2, func.__code__ era func.func_code func.__defaults__ era func.func_defaults . Las versiones posteriores de python 2, incluyendo 2.6 tienen ambos conjuntos de nombres, para ayudar a la transición de python 2 a python 3. __code__ el __code__ y __defaults__ más modernos. Si estás atrapado en un pitón más viejo, los conceptos son los mismos; sólo los nombres difieren.

Los valores predeterminados se almacenan en func.__defaults__ , y se recuperan cada vez que se llama a la función.

Por lo tanto, cuando define la función anterior, el cuerpo de la función se compila y almacena en las variables en __code__ , que se ejecutará más adelante, y los argumentos predeterminados se almacenan en __defaults__ . Cuando llama a la función, utiliza los valores en __defaults__ . Si esos valores se modifican por algún motivo, solo tiene la versión modificada disponible para usar.

Juegue alrededor de la definición de diferentes funciones en el intérprete interactivo, y vea lo que puede averiguar sobre cómo Python crea y usa las funciones.

Cuando define una función en Python con un parámetro de matriz, ¿cuál es el alcance de ese parámetro?

Este ejemplo está tomado del tutorial de Python:

def f(a, L=[]): L.append(a) return L print f(1) print f(2) print f(3)

Huellas dactilares:

[1] [1, 2] [1, 2, 3]

No estoy seguro si entiendo lo que está pasando aquí. ¿Significa esto que el alcance de la matriz está fuera de la función? ¿Por qué la matriz recuerda sus valores de llamada a llamada? Viniendo de otros idiomas, esperaría este comportamiento solo si la variable fuera estática. De lo contrario parece que debería reiniciarse cada vez. Y en realidad, cuando probé lo siguiente:

def f(a): L = [] L.append(a) return L

Obtuve el comportamiento que esperaba (la matriz se restableció en cada llamada).

Entonces, me parece que solo necesito la línea def f(a, L=[]): explicada: ¿cuál es el alcance de la variable L ?


El "problema" aquí es que L=[] solo se evalúa una vez , es decir, cuando se compila el archivo. Python recorre cada línea del archivo y lo compila. Cuando llega a la def con el parámetro predeterminado, crea esa instancia de la lista una vez.

Si coloca L = [] dentro del código de la función, la instancia no se crea en "tiempo de compilación" (en realidad, el tiempo de compilación también se puede llamar parte del tiempo de ejecución) porque Python compila el código de la función pero no la llama. Así que obtendrás una nueva instancia de lista porque la creación se realiza cada vez que llamas a la función (en lugar de una vez durante la compilación).

Una solución para ese problema es no usar objetos mutables como parámetros predeterminados, o solo instancias fijas como None :

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

Tenga en cuenta que en ambos casos que describió, el alcance de L es el alcance de la función.


El alcance de la variable L se comporta como usted espera.

El "problema" está en la lista que está creando con [] . Python no crea una nueva lista cada vez que llamas a la función. L se le asigna la misma lista cada vez que realiza una llamada, por lo que la función "recuerda" las llamadas anteriores.

Así que en efecto esto es lo que tienes:

mylist = [] def f(a, L=mylist): L.append(a) return L

El Tutorial de Python lo pone de esta manera :

El valor predeterminado se evalúa una sola vez. Esto hace una diferencia cuando el valor predeterminado es un objeto mutable, como una lista, un diccionario o instancias de la mayoría de las clases.

y sugiere la siguiente manera de codificar el comportamiento esperado:

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


El alcance es como usted esperaría.

Tal vez lo sorprendente es que el valor predeterminado solo se calcula una vez y se reutiliza, por lo que cada vez que llama a la función, obtiene la misma lista, no una lista nueva que se inicializa en [].

La lista se almacena en f.func_defaults .

def f(a, L=[]): L.append(a) return L print f(1) print f(2) print f(3) print f.func_defaults f.func_defaults = ([''foo''],) # Don''t do this! print f(4)

Resultado:

[1] [1, 2] [1, 2, 3] ([1, 2, 3],) [''foo'', 4]


Hay incluso menos "magia" de lo que podrías sospechar. Esto es equivalente a

m = [] def f(a, L=m): L.append(a) return L print f(1) print f(2) print f(3)

m solo se crea una vez.


Hay que tener en cuenta que Python es un lenguaje interpretado. Lo que está sucediendo aquí es cuando la función "f" está definida, crea la lista y la asigna al parámetro predeterminado "L" de la función "f". Más adelante, cuando llame a esta función, se utilizará la misma lista como parámetro predeterminado. En resumen, el código en la línea "def", solo se ejecuta una vez cuando se define la función. Este es un error común de Python, del cual he caído en mí mismo.

def f(a, L=[]): L.append(a) return L print f(1) print f(2) print f(3)

Aquí se han sugerido expresiones idiomáticas en otras respuestas para solucionar este problema. El que yo sugeriría es el siguiente:

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

Esto utiliza el o el cortocircuito para tomar la "L" que se pasó, o crear una nueva lista.

La respuesta a su pregunta sobre el alcance es que la "L" solo tiene un alcance dentro de la función "f", pero como los parámetros predeterminados solo se asignan una vez a una sola lista en lugar de cada vez que llama a la función, se comporta como si fuera el parámetro predeterminado. "L" tiene un alcance global.


La explicación se da en las respuestas a esta pregunta . Para resumir aquí:

Las funciones en Python son un tipo de objeto. Debido a que son un tipo de objeto, actúan como objetos cuando se crean instancias. Una función, si se define con un atributo mutable como un argumento predeterminado, es exactamente igual a una clase con un atributo estático que es una lista mutable.

Lennart Regebro tiene una buena explicación y la respuesta a la pregunta de Roberto Liffredo es excelente.

Para adaptar la respuesta de Lennart ... si tengo una clase de BananaBunch :

class BananaBunch: bananas = [] def addBanana(self, banana): self.bananas.append(banana) bunch = BananaBunch() >>> bunch <__main__.BananaBunch instance at 0x011A7FA8> >>> bunch.addBanana(1) >>> bunch.bananas [1] >>> for i in range(6): bunch.addBanana("Banana #" + i) >>> for i in range(6): bunch.addBanana("Banana #" + str(i)) >>> bunch.bananas [1, ''Banana #0'', ''Banana #1'', ''Banana #2'', ''Banana #3'', ''Banana #4'', ''Banana #5''] // And for review ... //If I then add something to the BananaBunch class ... >>> BananaBunch.bananas.append("A mutated banana") //My own bunch is suddenly corrupted. :-) >>> bunch.bananas [1, ''Banana #0'', ''Banana #1'', ''Banana #2'', ''Banana #3'', ''Banana #4'', ''Banana #5'', ''A mutated banana'']

¿Cómo se aplica esto a las funciones? Las funciones en Python son objetos . Esto vale la pena repetir. Las funciones en Python son objetos s.

Así que cuando creas una función, estás creando un objeto. Cuando le da a una función un valor predeterminado mutable, está rellenando el atributo de ese objeto con un valor mutable, y cada vez que llama a esa función está operando en el mismo atributo. Entonces, si está utilizando una llamada mutable (como adjuntar), entonces está modificando el mismo objeto, como si estuviera agregando bananas al objeto bunch .