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

我一直在尝试学习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)
            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]