pyplot python functional-programming closures

pyplot - ¿Puedes explicar los cierres(ya que se relacionan con Python)?



title plt python (13)

He leído mucho sobre cierres y creo que los entiendo, pero sin nublar la imagen para mí y para los demás, espero que alguien pueda explicar los cierres de la manera más clara y concisa posible. Estoy buscando una explicación simple que me ayude a entender dónde y por qué querría usarlos.


Aquí hay un caso de uso típico para cierres: devoluciones de llamada para elementos GUI (esto sería una alternativa a la subclase de la clase de botón). Por ejemplo, puede construir una función que se invocará en respuesta a la pulsación de un botón, y "cerrar" las variables relevantes en el ámbito principal que son necesarias para procesar el clic. De esta forma, puede conectar interfaces bastante complicadas desde la misma función de inicialización, creando todas las dependencias en el cierre.


Aquí hay un ejemplo de cierres de Python3

def closure(x): def counter(): nonlocal x x += 1 return x return counter; counter1 = closure(100); counter2 = closure(200); print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 2 " + str(counter2())) print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 2 " + str(counter2())) # result i from closure 1 101 i from closure 1 102 i from closure 2 201 i from closure 1 103 i from closure 1 104 i from closure 1 105 i from closure 2 202


En Python, un cierre es una instancia de una función que tiene variables ligadas a ella inmutablemente.

De hecho, el modelo de datos lo explica en su descripción del atributo de funciones '' __closure__ :

Ninguno o una tupla de celdas que contienen enlaces para las variables libres de la función. Solo lectura

Para demostrar esto:

def enclosure(foo): def closure(bar): print(foo, bar) return closure closure_instance = enclosure(''foo'')

Claramente, sabemos que ahora tenemos una función apuntada desde el nombre de la variable closure_instance . Ostensiblemente, si lo llamamos con un objeto, bar , debe imprimir la cadena, ''foo'' y cualquiera que sea la representación de cadena de la bar .

De hecho, la cadena ''foo'' está vinculada a la instancia de la función, y podemos leerla directamente aquí, accediendo al atributo cell_contents de la primera (y única) celda en la tupla del atributo __closure__ :

>>> closure_instance.__closure__[0].cell_contents ''foo''

Como un aparte, los objetos de celda se describen en la documentación de C API:

Los objetos "Cell" se usan para implementar las variables a las que hacen referencia los múltiples ámbitos

Y podemos demostrar el uso de nuestro cierre, teniendo en cuenta que ''foo'' está atrapado en la función y no cambia:

>>> closure_instance(''bar'') foo bar >>> closure_instance(''baz'') foo baz >>> closure_instance(''quux'') foo quux

Y nada puede cambiarlo:

>>> closure_instance.__closure__ = None Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: readonly attribute

Funciones parciales

El ejemplo dado utiliza el cierre como una función parcial, pero si este es nuestro único objetivo, se puede lograr el mismo objetivo con functools.partial

>>> from __future__ import print_function # use this if you''re in Python 2. >>> partial_function = functools.partial(print, ''foo'') >>> partial_function(''bar'') foo bar >>> partial_function(''baz'') foo baz >>> partial_function(''quux'') foo quux

También hay cierres más complicados que no encajarían en el ejemplo de la función parcial, y los demostraré más a medida que el tiempo lo permita.


Es simple: una función que hace referencia a variables de un ámbito que lo contiene, potencialmente después del flujo de control, ha abandonado ese ámbito. Ese último bit es muy útil:

>>> def makeConstantAdder(x): ... constant = x ... def adder(y): ... return y + constant ... return adder ... >>> f = makeConstantAdder(12) >>> f(3) 15 >>> g = makeConstantAdder(4) >>> g(3) 7

Tenga en cuenta que 12 y 4 han "desaparecido" dentro de f y g, respectivamente, esta característica es lo que hace f y g cierres adecuados.


La mejor explicación que he visto de un cierre fue explicar el mecanismo. Fue algo como ésto:

Imagine su pila de programas como un árbol degenerado donde cada nodo tiene un solo hijo y el nodo de hoja individual es el contexto de su procedimiento de ejecución actual.

Ahora relaje la restricción de que cada nodo puede tener solo un hijo.

Si haces esto, puedes tener un constructo (''rendimiento'') que puede regresar de un procedimiento sin descartar el contexto local (es decir, no lo saca de la pila cuando regresas). La próxima vez que se invoca el procedimiento, la invocación recoge el marco de pila (árbol) anterior y continúa ejecutándose donde lo dejó.


Me gusta esta definición aproximada y sucinta :

Una función que puede referirse a entornos que ya no están activos.

Yo agregaría

Un cierre le permite vincular variables en una función sin pasarlas como parámetros .

Los decoradores que aceptan parámetros son un uso común para los cierres. Los cierres son un mecanismo de implementación común para ese tipo de "fábrica de funciones". Con frecuencia elijo usar cierres en el Patrón de Estrategia cuando la estrategia es modificada por datos en tiempo de ejecución.

En un lenguaje que permite la definición de bloques anónimos, por ejemplo, Ruby, C #, se pueden usar cierres para implementar (lo que equivale a) nuevas estructuras de control novedosas. La falta de bloques anónimos es una de las limitaciones de los cierres en Python .


Me gustaría compartir mi ejemplo y una explicación sobre los cierres. Hice un ejemplo de python y dos figuras para demostrar estados de pila.

def maker(a, b, n): margin_top = 2 padding = 4 def message(msg): print(''/n’ * margin_top, a * n, '' ‘ * padding, msg, '' ‘ * padding, b * n) return message f = maker(''*'', ''#'', 5) g = maker('''', ''♥’, 3) … f(''hello'') g(‘good bye!'')

El resultado de este código sería el siguiente:

***** hello #####  good bye! ♥♥♥

Aquí hay dos figuras para mostrar las pilas y el cierre unido al objeto de la función.

cuando la función es devuelta por el fabricante

cuando la función se llama después

Cuando se llama a la función a través de un parámetro o una variable no local, el código necesita enlaces de variables locales como margin_top, padding y a, b, n. Para garantizar el funcionamiento del código de la función, se debe poder acceder al marco de la pila de la función creadora que desapareció hace mucho tiempo, que se respalda en el cierre que podemos encontrar junto con el objeto de función del mensaje.


Nunca he escuchado que las transacciones se usen en el mismo contexto que explicar qué es un cierre y realmente no hay ninguna semántica de transacción aquí.

Se llama cierre porque "cierra" la variable externa (constante), es decir, no es solo una función sino un cerramiento del entorno donde se creó la función.

En el siguiente ejemplo, llamar al cierre g después de cambiar x también cambiará el valor de x dentro de g, ya que g cierra sobre x:

x = 0 def f(): def g(): return x * 2 return g closure = f() print(closure()) # 0 x = 2 print(closure()) # 4


Para mí, los "cierres" son funciones que son capaces de recordar el entorno en el que se crearon. Esta funcionalidad le permite usar variables o métodos dentro del cierre que, de otra manera, no podría usar porque ya no existen o porque están fuera de alcance debido al alcance. Veamos este código en ruby:

def makefunction (x) def multiply (a,b) puts a*b end return lambda {|n| multiply(n,x)} # => returning a closure end func = makefunction(2) # => we capture the closure func.call(6) # => Result equal "12"

funciona incluso cuando ambos métodos, "multiplicar" y "x", ya no existen. Todo porque la capacidad de cierre para recordar.


Para ser sincero, entiendo los cierres perfectamente bien, excepto que nunca he tenido claro qué es exactamente lo que es el "cierre" y cuál es el "cierre" al respecto. Te recomiendo que te rindas buscando cualquier lógica detrás de la elección del término.

De todos modos, aquí está mi explicación:

def foo(): x = 3 def bar(): print x x = 5 return bar bar = foo() bar() # print 5

Una idea clave aquí es que el objeto de función devuelto por foo conserva un enlace al var ''x'' local, aunque ''x'' ha salido del alcance y debe haber desaparecido. Este gancho es para la var en sí, no solo para el valor que var tenía en ese momento, por lo que cuando se llama a la barra, imprime 5, no 3.

También tenga en claro que Python 2.x tiene un cierre limitado: no hay manera de que pueda modificar ''x'' dentro de ''barra'' porque escribir ''x = bla'' declararía una ''x'' local en la barra, no asignaría a ''x'' de foo . Este es un efecto secundario de la asignación = declaración de Python. Para evitar esto, Python 3.0 introduce la palabra clave no local:

def foo(): x = 3 def bar(): print x def ack(): nonlocal x x = 7 x = 5 return (bar, ack) bar, ack = foo() ack() # modify x of the call to foo bar() # print 7


todos hemos usado decoradores en python. Son buenos ejemplos para mostrar cuáles son las funciones de cierre en Python.

class Test(): def decorator(func): def wrapper(*args): b = args[1] + 5 return func(b) return wrapper @decorator def foo(val): print val + 2 obj = Test() obj.foo(5)

aquí el valor final es 12

Aquí, la función contenedora puede acceder al objeto func porque el contenedor es "cierre léxico", puede acceder a sus atributos principales. Por eso, puede acceder al objeto func.


Cierre de cierres

Los objetos son datos con métodos adjuntos, los cierres son funciones con datos adjuntos.

def make_counter(): i = 0 def counter(): # counter() is a closure nonlocal i i += 1 return i return counter c1 = make_counter() c2 = make_counter() print (c1(), c1(), c2(), c2()) # -> 1 2 1 2


# A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory. # Defining a closure # This is an outer function. def outer_function(message): # This is an inner nested function. def inner_function(): print(message) return inner_function # Now lets call the outer function and return value bound to name ''temp'' temp = outer_function("Hello") # On calling temp, ''message'' will be still be remembered although we had finished executing outer_function() temp() # Technique by which some data(''message'') that remembers values in enclosing scopes # even if they are not present in memory is called closures # Output: Hello

Los criterios para cumplir con Closures son:

  1. Debemos tener una función anidada.
  2. La función anidada debe hacer referencia al valor definido en la función adjunta.
  3. La función de envolvente debe devolver la función anidada.

# Example 2 def make_multiplier_of(n): # Outer function def multiplier(x): # Inner nested function return x * n return multiplier # Multiplier of 3 times3 = make_multiplier_of(3) # Multiplier of 5 times5 = make_multiplier_of(5) print(times5(3)) # 15 print(times3(2)) # 6