【问题标题】:Closures in Python - an example [duplicate]Python中的闭包 - 一个例子[重复]
【发布时间】:2013-03-17 20:13:27
【问题描述】:

我在一个循环中多次执行一个动作,想知道我走了多远。我正在尝试制作一个进度报告功能,应该像这样:

def make_progress_report(n):
    i = 0
    def progress_report():
        i = i + 1
        if i % n == 0:
            print i
    return progress_report

pr = make_progress_report(2)
pr()
pr()  # 2
pr()
pr()  # 4

此代码不起作用。具体来说,我为i 得到一个UnboundLocalError。我应该如何修改它才能正常工作?

【问题讨论】:

  • @delnan 正确,我四处搜索,但是关于关闭的问题太多了,我没有全部阅读。我应该根据您链接的问题调整代码并将其粘贴到此处吗?

标签: python closures


【解决方案1】:

这里有 3 个选项:

  1. 为您的计数器使用列表:

    def make_progress_report(n):
        i = [0]
        def progress_report():
            i[0] = i[0] + 1
            if i[0] % n == 0:
                print i[0]
        return progress_report
    
  2. 使用 itertools.count 跟踪您的计数器:

    from itertools import count
    def make_progress_report(n):
        i = count(1)
        def progress_report():
            cur = i.next()
            if cur % n == 0:
                print cur
        return progress_report
    
  3. 使用 nonlocal 作为您的计数器(仅限 Python 3+!):

    def make_progress_report(n):
        i = 0
        def progress_report():
            nonlocal i
            i = i + 1
            if i % n == 0:
                print i
        return progress_report
    

【讨论】:

  • 使用列表而不是数字的原因是否有效,因为列表是可变的?
  • 是的……是的。基本上,在内部范围内,Python 不允许您重新分配其外部范围内的任何变量,因此您不能直接将i 重新分配给i+1。但是,当您修改列表时,对列表的引用保持不变,只是其内容发生了变化。
【解决方案2】:

您可以考虑使用生成器:

def progress_report(n):
    i = 0
    while 1:
        i = i+1
        if i % n == 0:
            print i
        yield # continue from here next time

pr = progress_report(2)

next(pr)
next(pr)
next(pr)
next(pr)

【讨论】:

    【解决方案3】:

    因此,progress_report 不会对变量 i 关闭。你可以这样检查...

    >>> def make_progress_report(n):
    ...     i=0
    ...     def progress_report():
    ...             i += 1
    ...             if i % n == 0:
    ...                     print i
    ...     return progress_report
    ...
    >>> pr = make_progress_report(2)
    >>> pr.__closure__
    (<cell at 0x1004a5be8: int object at 0x100311ae0>,)
    >>> pr.__closure__[0].cell_contents
    2
    

    您会注意到 pr 的结尾只有一项。这是您最初为n 传递的值。 i 值不是闭包的一部分,因此在函数定义之外,i 不再在范围内。

    这里是关于 Python 闭包的精彩讨论:http://www.shutupandship.com/2012/01/python-closures-explained.html

    【讨论】:

      【解决方案4】:

      再看看你是如何定义你的闭包的。 n 应该在定义闭包时传入...举个例子:

      #!/usr/env python
      def progressReportGenerator(n):
          def returnProgress(x):
              if x%n == 0:
                  print "progress: %i" % x
          return returnProgress
      
      complete = False
      i = 0 # counter
      n = 2 # we want a progress report every 2 steps
      
      getProgress = progressReportGenerator(n)
      
      while not complete:
          i+=1 # increment step counter
      
          # your task code goes here..
      
          getProgress(i) # how are we going?
      
          if i == 20: # stop at some arbtirary point... 
              complete = True
              print "task completed"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-07
        • 1970-01-01
        • 2012-10-13
        • 2013-12-26
        • 1970-01-01
        相关资源
        最近更新 更多