resultado - parametros por omision python
¿Cómo funcionan las funciones anidadas en Python? (9)
Básicamente estás creando un cierre .
En informática, un cierre es una función de primera clase con variables libres que están vinculadas en el entorno léxico. Se dice que dicha función está "cerrada" sobre sus variables libres.
Lectura relacionada: cierres: ¿por qué son tan útiles?
Un cierre es simplemente una forma más conveniente de dar a una función acceso al estado local.
De http://docs.python.org/reference/compound_stmts.html :
Nota del programador: las funciones son objetos de primera clase. Un formulario ''def'' ejecutado dentro de una definición de función define una función local que puede devolverse o transmitirse. Las variables gratuitas utilizadas en la función anidada pueden acceder a las variables locales de la función que contiene la def. Ver la sección Denominación y enlace para más detalles.
def maker(n):
def action(x):
return x ** n
return action
f = maker(2)
print(f)
print(f(3))
print(f(4))
g = maker(3)
print(g(3))
print(f(3)) # still remembers 2
¿Por qué la función anidada recuerda el primer valor 2
aunque maker()
haya regresado y salido por el momento en que se llama a action()
?
Cuando crea una función con la palabra clave def, está haciendo exactamente eso: está creando un nuevo objeto de función y asignándolo a una variable. En el código que le dio está asignando ese nuevo objeto de función a una variable local llamada acción.
Cuando lo llamas por segunda vez estás creando un segundo objeto de función. Así que f apunta al primer objeto de función (cuadrado-el-valor) y g apunta al segundo objeto de función (cubo-el-valor). Cuando Python ve "f (3)" lo lleva a significa "ejecutar el objeto de función señalado como variable f y pasarle el valor 3". fyg y diferentes objetos de función y así devuelven valores diferentes.
Eso es lo que se llama " closure ". En pocas palabras, para la mayoría si no todos los lenguajes de programación que tratan las funciones como objetos de primera clase , las variables que se usan dentro de un objeto de función se incluyen (es decir, se recuerdan) siempre que la función aún esté activa. Es un concepto poderoso si sabes cómo usarlo.
En su ejemplo, la función de action
anidada usa la variable n
por lo que forma un cierre alrededor de esa variable y la recuerda para llamadas a funciones posteriores.
Estás definiendo DOS funciones. Cuando usted llama
f = maker(2)
está definiendo una función que devuelve dos veces el número, por lo que
f(2) --> 4
f(3) --> 6
Luego, defines OTRA FUNCIÓN DIFERENTE
g = maker(3)
que devuelve tres veces el número
g(3) ---> 9
Pero son DOS funciones diferentes, no es la misma función a la que se hace referencia, cada una es independiente. Incluso en el alcance dentro de la función ''maker'' se llama igual, no es la misma función, cada vez que llame a maker()
está definiendo una función diferente. Es como una variable local, cada vez que llama a la función toma el mismo nombre, pero puede contener diferentes valores. En este caso, la variable ''acción'' contiene una función (que puede ser diferente)
La gente respondió correctamente sobre el cierre, es decir: el valor válido para "n" dentro de acción es el último valor que tenía cuando se llamaba "creador".
Una forma fácil de superar esto es hacer que su freevar (n) sea una variable dentro de la función "acción", que recibe una copia de "n" en el momento en que se ejecuta:
La forma más fácil de hacerlo es establecer "n" como un parámetro cuyo valor predeterminado es "n" en el momento de la creación. Este valor para "n" permanece fijo porque los parámetros predeterminados para una función se almacenan en una tupla que es un atributo de la función misma (action.func_defaults en este caso):
def maker(n):
def action(x, k=n):
return x ** k
return action
Uso:
f = maker(2) # f is action(x, k=2)
f(3) # returns 3^2 = 9
f(3,3) # returns 3^3 = 27
Porque en el momento cuando creas la función, n
era 2
, entonces tu función es:
def action(x):
return x ** 2
Cuando llame a f (3), x
se establece en 3
, por lo que su función devolverá 3 ** 2
Puede verlo como todas las variables que se originan en la función primaria reemplazada por su valor real dentro de la función secundaria. De esta forma, no es necesario realizar un seguimiento del alcance de la función principal para que la función hija se ejecute correctamente.
Véalo como "crear dinámicamente una función".
def maker(n):
def action(x):
return x ** n
return action
f = maker(2)
--> def action(x):
--> return x ** 2
Este es un comportamiento básico en Python, hace lo mismo con múltiples asignaciones.
a = 1
b = 2
a, b = b, a
Python lee esto como
a, b = 2, 1
Básicamente inserta los valores antes de hacer cualquier cosa con ellos.
Un uso es devolver una función que mantiene un parámetro.
def outer_closure(a):
# parm = a <- saving a here isn''t needed
def inner_closure():
#return parm
return a # <- a is remembered
return inner_closure
# set parm to 5 and return address of inner_closure function
x5 = outer_closure(5)
x5()
>5
x6 = outer_closure(6)
x6()
>6
# x5 inner closure function instance of parm persists
x5()
>5
Veamos tres razones comunes para escribir funciones internas.
1. Cierres y funciones de fábrica
El valor en el alcance adjunto se recuerda incluso cuando la variable se sale del alcance o la función se elimina del espacio de nombre actual.
def print_msg(msg):
"""This is the outer enclosing function"""
def printer():
"""This is the nested function"""
print(msg)
return printer # this got changed
Ahora intentemos llamar a esta función.
>>> another = print_msg("Hello")
>>> another()
Hello
Eso es inusual. La función print_msg()
se llamó con la cadena "Hello"
y la función devuelta se vinculó al nombre another
. Al llamar a another()
, el mensaje aún se recordaba, aunque ya habíamos terminado de ejecutar la función print_msg()
. Esta técnica por la cual algunos datos ( "Hello"
) se adjuntan al código se llama cierre en Python.
Entonces, ¿para qué sirven los cierres? Los cierres pueden evitar el uso de valores globales y proporcionan algún tipo de ocultación de datos. También puede proporcionar una solución orientada a objetos para el problema. Cuando hay pocos métodos (un método en la mayoría de los casos) para implementar en una clase, los cierres pueden proporcionar soluciones alternativas y más elegantes. Reference
2. Encapsulación:
El concepto general de encapsulación es ocultar y proteger el mundo interno del exterior. Aquí se puede acceder a las funciones internas solo dentro del exterior y están protegidas de cualquier cosa que ocurra fuera de la función.
3. Manteniéndolo SECO
Tal vez tenga una función gigante que realiza el mismo fragmento de código en numerosos lugares. Por ejemplo, puede escribir una función que procese un archivo y desee aceptar un objeto de archivo abierto o un nombre de archivo:
def process(file_name):
def do_stuff(file_process):
for line in file_process:
print(line)
if isinstance(file_name, str):
with open(file_name, ''r'') as f:
do_stuff(f)
else:
do_stuff(file_name)
Para más información, puede referirse a this blog.