font python closures lexical-scope

font - Cierres en Python



subplot title python (6)

¿Probablemente porque quieres obtener global mientras no es global? Por cierto, apply está en desuso, use fn (* args) en su lugar.

def memoize(fn): def get(key): return (False,) def vset(key, value): def newget(ky): if key==ky: return (True, value) return get(ky) get = newget def mfun(*args): cache = get(args) if (cache[0]): return cache[1] val = fn(*args) vset(args, val) return val return mfun def fib(x): if x<2: return x return fib(x-1)+fib(x-2) def fibm(x): if x<2: return x return fibm(x-1)+fibm(x-2) fibm = memoize(fibm)

He estado tratando de aprender Python, y si bien estoy entusiasmado con el uso de cierres en Python, he estado teniendo problemas para hacer que algún código funcione correctamente:

def memoize(fn): def get(key): return (False,) def vset(key, value): global get oldget = get def newget(ky): if key==ky: return (True, value) return oldget(ky) get = newget def mfun(*args): cache = get(args) if (cache[0]): return cache[1] val = apply(fn, args) vset(args, val) return val return mfun def fib(x): if x<2: return x return fib(x-1)+fib(x-2) def fibm(x): if x<2: return x return fibm(x-1)+fibm(x-2) fibm = memoize(fibm)

Básicamente, lo que se supone que debe hacer es usar cierres para mantener el estado memorizado de la función. Me doy cuenta de que probablemente haya muchas formas más rápidas, fáciles de leer y, en general, más "pitónicas" para implementar esto; sin embargo, mi objetivo es comprender exactamente cómo funcionan los cierres en Python, y cómo se diferencian de Lisp, por lo que no estoy interesado en soluciones alternativas, simplemente por qué mi código no funciona y qué puedo hacer (si es que hay algo) para corregirlo. eso.

El problema con el que me estoy fibm es cuando trato de usar fibm : Python insiste en que get no está definido:

Python 2.6.1 (r261:67515, Feb 1 2009, 11:39:55) [GCC 4.0.1 (Apple Inc. build 5488)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import memoize >>> memoize.fibm(35) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "memoize.py", line 14, in mfun cache = get(args) NameError: global name ''get'' is not defined >>>

Al ver que soy nuevo en Python, no sé si he hecho algo mal, o si esto es solo una limitación del lenguaje. Espero que sea el primero. :-)


Desea poner global get al comienzo de cada función (excepto get ).

el def get es una asignación al nombre get , por lo que quieres ser declarado global antes de eso.

Poner global get en mfun y vset hace que funcionen. No puedo señalar las reglas de alcance que hacen que esto sea necesario, pero funciona ;-)

Tus opiniones son bastante lisas también ... :)


El problema está en su alcance, no en sus cierres. Si le apetece leer algo, entonces puede intentar http://www.python.org/dev/peps/pep-3104/ .

Si ese no es el caso, aquí está la explicación simple:

El problema está en la declaración global get . global refiere al alcance más externo, y como no hay ninguna función global get , throws.

Lo que necesita es un especificador de acceso para las variables en el alcance adjunto, y no el alcance global.

En Python 3.0, como he probado, la palabra clave nonlocal es exactamente lo que necesita, en lugar de global .

nonlocal get ...

En Python 2.x, oldget referencias global get y oldget y funciona correctamente.


Get no es global, sino local para la función circundante, por eso falla la declaración global .

Si elimina el global , aún falla, porque no puede asignarle el nombre de la variable capturada. Para solucionarlo, puede usar un objeto como la variable capturada por sus cierres y luego simplemente cambiar las propiedades de ese objeto:

class Memo(object): pass def memoize(fn): def defaultget(key): return (False,) memo = Memo() memo.get = defaultget def vset(key, value): oldget = memo.get def newget(ky): if key==ky: return (True, value) return oldget(ky) memo.get = newget def mfun(*args): cache = memo.get(args) if cache[0]: return cache[1] val = apply(fn, args) vset(args, val) return val return mfun

De esta forma, no es necesario que asigne los nombres de las variables capturadas, pero obtenga lo que desea.


def memoize(fn): get = [lambda key: (False, None)] def vset(args): value = fn(*args) oldget = get[0] def newget(key): if args == key: return (True, value) return oldget(key) get[0] = newget return value def mfun(*args): found, value = get[0](args) if found: return value return vset(args) return mfun CALLS = 0 def fib(x): global CALLS CALLS += 1 if x<2: return x return fib(x-1)+fib(x-2) @memoize def fibm(x): global CALLS CALLS += 1 if x<2: return x return fibm(x-1)+fibm(x-2) CALLS = 0 print "fib(35) is", fib(35), "and took", CALLS, "calls" CALLS = 0 print "fibm(35) is", fibm(35), "and took", CALLS, "calls"

La salida es:

fib(35) is 9227465 and took 29860703 calls fibm(35) is 9227465 and took 36 calls

Similar a otras respuestas, sin embargo, esta funciona. :)

El cambio importante del código en la pregunta es la asignación a un no local no local (get); sin embargo, también realicé algunas mejoras al tratar de mantener su * tos * rotura * tos * cierre. Por lo general, el caché es un dict en lugar de una lista vinculada de cierres.


Creo que la mejor manera sería:

class Memoized(object): def __init__(self,func): self.cache = {} self.func = func def __call__(self,*args): if args in self.cache: return cache[args] else: self.cache[args] = self.func(*args) return self.cache[args]