closures

Chema Cortes pych3m4 en gmail.com
Lun Jul 23 05:47:14 CEST 2007


El 22/07/07, Carles Pina i Estany <carles en pina.cat> escribió:

> Estoy intentando entender las closures. En la Wikipedia (http://en.wikipedia.org/wiki/Closure_(computer_science) (al principio) puedo ver de
> qué se trata. Entiendo que es que el comportamiento de una función está
> determinado, en parte, por el comportamiento de datos/código externo de
> esta función. Básicamente, que puede acceder fuera de la función.

Umm! Dicho así parece otra cosa de lo que es. Una función nunca
debería acceder "fuera" de sus dominios bajo ninguna circunstancia.

Normalmente, las funciones se definen en el mismo entorno donde se
ejecutan; pero hay ocasiones en las que no ocurre así, como en el caso
de las funciones anidadas donde una función se define dentro del
entorno local de otra. En este caso, la función anidada se ejecutará
en el entorno donde se ha definido, fuera de los ojos del resto del
programa. Ésto es lo que se conoce por "clausura".

Así, podemos definir "clausura" como el entorno donde se define una
función y que sólo va a ser "visible" por esta función. Y por
"entorno", en python, debería entenderse el conjunto de objetos de los
que contamos con alguna "referencia" a ellos (en otros lenguajes se
hablan de otras cosas como stacks, heaps, etc).


> Leo aquí (ojo, del año 2003):
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/67618
> Que python no tiene closures completas como Ruby por este motivo:
> -----
> But in Python, the inner function only has access to the _object_, not
> to the original variable which refers to the object
> -----
>
> Así, en Python hay closures "parciales"? He entendido bien que las
> clsorues son algo tan simple como que una función pueda acceder a datos
> de fuera de la función, de quien llama la función?

¡ÉSO ES PECADO!

LLámalas, si quieres, clausuras "parciales"; pero, al fin y al cabo,
son clausuras. No siempre es buena idea permitir que las funciones
anidadas alteren variables que no sean suyas.

¿Qué problema hay en encapsular la función, junto a las variable a las
que accede dentro de una clase (function wrapper)?

def make_counter(start):
	class counter:
		def __init__(self,start):
			self._start=start
		def __call__(self,inc):
			self._start+=inc
			return self._start
	return counter(start)

c=make_counter(0)
print c(3)
print c(2)


Y si no, con generadores e iteradores seguro que también iría bien.

def make_counter2(start):
  while True:
    inc=(yield start)
    if inc is not None:
      start+=inc

c=make_counter2(0)
c.next()
print c.send(3)
print c.send(2)


Una versión "forzada", que viene a emular lo que se entendería por
clausura "completa":

def make_counter(start):
	def counter(inc):
		global start
		start+=inc
		return start
        counter.func_globals.clear()
	counter.func_globals.update(locals())
	return counter

c=make_counter(0)
print c(3)
print c(2)


Una versión más moderada que usa atributos de función:

def make_counter(start):
	def counter(inc):
		f.start+=inc
		return f.start
	f.start=start
	return counter

Y la versión de libro:

def make_counter(start):
        s=[start]
	def counter(inc):
		s[0]+=inc
		return s[0]
	return counter




Más información sobre la lista de distribución Python-es