Python中的闭包

2024-05-11 04:59:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在努力学习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中是如何工作的,以及它们与Lisp的区别,所以我对其他解决方案不感兴趣,只关心为什么我的代码不能工作,以及我可以做些什么(如果有的话)来修复它。在

我遇到的问题是当我试图使用fibm-Python坚持认为{}没有定义:

^{pr2}$

由于我是Python的新手,我不知道我是否做错了什么,或者这只是语言的一个限制。我希望是前者。:-)


Tags: key代码cachegetreturnifdefargs
3条回答

问题在于你的范围界定,而不是你的闭包。如果你想读一些繁重的书,那么你可以试试http://www.python.org/dev/peps/pep-3104/。在

如果不是这样的话,解释如下:

问题出在global get语句中。global引用最外层的作用域,由于没有任何全局函数get,它抛出。在

您需要的是封闭范围内变量的访问说明符,而不是全局范围。在

在Python3.0中,正如我所测试的,nonlocal关键字正是您所需要的,它代替了global。在

nonlocal get
...

在Python2.x中,我只删除了global get和{}引用,它可以正常工作。在

您希望将global get放在每个函数的开头(除了get本身)。在

def get是对名称get的赋值,因此您希望get在此之前声明为global。在

global get放在mfun和vset中可以使它们工作。我不能指出使这一点成为必要的范围界定规则,但它是有效的;-)

你的警察也很健忘。。。:)

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"

输出为:

^{pr2}$

与其他答案类似,但这一个有效。:)

问题中的代码的重要变化是分配给非全局非本地(get);但是,我在尝试维护*咳嗽*break*keak*闭包的使用。通常缓存是dict而不是闭包的链接列表。在

相关问题 更多 >