当前位置:  开发笔记 > 编程语言 > 正文

Python中的闭包

如何解决《Python中的闭包》经验,为你挑选了2个好方法。

我一直在努力学习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)

基本上,这应该做的是使用闭包来维持函数的memoized状态.我意识到可能有更快,更容易阅读,并且通常更多'Pythonic'方式来实现这一点; 但是,我的目标是准确理解闭包在Python中是如何工作的,以及它们与Lisp的区别,所以我对替代解决方案不感兴趣,为什么我的代码不起作用以及我可以做什么(如果有的话)修复它.

我遇到的问题是当我尝试使用时fibm- Python坚持get没有定义:

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 "", line 1, in 
  File "memoize.py", line 14, in mfun
    cache = get(args)
NameError: global name 'get' is not defined
>>> 

看到我是Python的新手,我不知道我做错了什么,或者这只是语言的限制.我希望它是前者.:-)



1> sykora..:

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

如果不是这样,这里有一个简单的解释:

问题出在声明中global get.global它指的是最外层的范围,并且由于没有任何全局函数get,它会抛出.

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

在python 3.0中,正如我测试的那样,nonlocal关键字正是你所需要的,而不是代替global.

nonlocal get
...

在python 2.x中,我刚删除global getoldget引用并且它正常工作.



2> 小智..:
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

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

问题中代码的重要变化是分配给非全局非本地(get); 不过,我也试图保持你做了一些改进***咳嗽*封闭使用.通常,缓存是dict而不是闭包的链接列表.

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有