Python中的闭包
我一直在尝试学习Python,虽然我对在Python中使用闭包很感兴趣,但我在让一些代码正常工作方面遇到了困难:Python中的闭包,python,closures,lexical-scope,Python,Closures,Lexical Scope,我一直在尝试学习Python,虽然我对在Python中使用闭包很感兴趣,但我在让一些代码正常工作方面遇到了困难: 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
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)
由于我是Python新手,我不知道自己是否做错了什么,或者这只是该语言的一个局限性。我希望是前者。:-) 您希望将
global get
放在每个函数的开头(除了get
本身)
def get
是对名称get
的赋值,因此您希望在此之前将get声明为全局
将global get
放在mfun和vset中可以使它们工作。我不能指出使这成为必要的范围规则,但它是有效的;-)
你的妻子也很口齿不清……:) 问题在于您的范围,而不是闭包。如果你想重读一些书,那么你可以试试 如果不是这样,下面是简单的解释: 问题出现在语句
global get
中global
指的是最外层的作用域,因为没有任何全局函数get
,所以抛出
您需要的是封闭范围内变量的访问说明符,而不是全局范围
在python 3.0中,正如我所测试的那样,nonlocal
关键字正是您所需要的,而不是global
nonlocal get
...
在Python2.x中,我刚刚删除了
global get
和oldget
引用,它工作正常。可能是因为您想要全局get,而它不是全局get?
顺便说一下,不推荐使用apply,请改用fn(*args)
def记忆(fn):
def get(密钥):
返回(False,)
def vset(键、值):
def newget(ky):
if key==ky:return(True,value)
返回获取(ky)
get=newget
def mfun(*参数):
cache=get(args)
if(缓存[0]):返回缓存[1]
val=fn(*args)
vset(args,val)
返回值
返回mfun
def纤维(x):
如果x
与其他答案类似,但此答案有效。:)
问题中代码的重要变化是分配给非全局非局部(get);但是,我在尝试维护您的*
cough*
break*
cough*
闭包使用时也做了一些改进。通常,缓存是一个dict,而不是闭包的链表。Get
不是全局的,而是周围函数的局部,这就是全局
声明失败的原因
如果删除全局
,它仍然会失败,因为您无法为捕获的变量名赋值。要解决这个问题,您可以使用对象作为闭包捕获的变量,而不仅仅是更改该对象的属性:
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
这样,您不需要为捕获的变量名赋值,但仍然可以得到您想要的。我认为最好的方法是:
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]
隐马尔可夫模型。。在我的机器上,将全局get放入get和vset无法工作。将它添加到memoize的开头本身是有效的,但这会导致每次调用memoize都使用相同的get函数(这是不可取的)。自我提示:喝咖啡,醒来,然后回答;-)谢谢你给我关于申请的提示。然而,运行上面修改过的代码可以给出正确的答案,但根本不使用记忆。(您可以通过运行fibm(35)之类的程序来验证这一点,如果将其记住,应该是接近瞬时的。x=y分配(或创建)局部变量x,除非该函数中的某个地方有全局语句。因此这是语言中的一个缺陷。我希望您不会这么说:-(一种可能的解决方法是将值存储在可变结构(如列表)中并进行修改,但这很不妥当。我想要一个2.X版本以实现兼容性,但我可能不得不跳转到3000。有一种解决方法,非常简单,除非它与您的纯度概念相冲突——只需make get()一个全局函数。也就是说,我必须说,你的记忆方法对我来说太复杂了:)它如何正常工作?只删除“global get”和“oldget=get”行,它从不使用保存的值。在newget中,将“oldget”更改为“get”。我假设“正常工作”,意味着它返回了正确的答案(尽管没有回忆录)。这并不理想,但我想解决方法已经足够好了。谢谢你也对代码进行了一点润色。IMHO,除了学习Python如何处理闭包外,使用这段代码做任何事情都是注定要失败的。其他更改比任何其他更改都更能将你推向正确的方向。这是主要目标。如果我用Python编写代码,我会相反,我使用se字典,但我非常喜欢在一条语句中设置多个值,并使用fn(*args)代替apply。
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"
fib(35) is 9227465 and took 29860703 calls
fibm(35) is 9227465 and took 36 calls
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
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]