Python封闭+全局陌生

时间:2012-06-22 21:07:55

标签: python

我希望这个小小的片段能够打印“为什么这不起作用?”有人可以帮助我理解为什么这不起作用,因为我的期望?我正在使用Python 2.6,如果这很重要的话。

class WhyDoesntThisWork(object):
  def outer(self):
    acc = ''
    def inner(msg):
      global acc
      acc = acc + msg
    inner("Why doesn't")
    inner(" this work?")
    print acc
WhyDoesntThisWork().outer()
  • 如果我添加global语句,我会收到NameError: global name 'acc' is not defined
  • 如果我不包含global声明,我会收到UnboundLocalError: local variable 'acc' referenced before assignment

2 个答案:

答案 0 :(得分:8)

我不知道为什么上面那么多评论都包含正确答案,没有人敢写出实际答案,所以我会在此做。

class ThisWorksNow(object):
  def outer(self):
    acc = []
    def inner(msg):
      acc.append(msg)
    inner("Why doesn't")
    inner(" this work?")
    print "".join(acc)
ThisWorksNow().outer()

有什么区别?

为闭包中的对象指定名称在Python 2.x中不起作用,因为缺少Py3关键字nonlocal,所以我们必须找到一种解决方法。

如果我们必须保持名称到对象绑定的常量,我们必须改变别的东西。在这种情况下,它是我们添加要添加的内容的对象。

print行不是很优雅;也许打印其内容连接的对象可能更合适。

class StringBuilder(list): # class name stolen from Java
    def __str__(self):
        """this makes the object printable in a way which represents the concatenated string"""
        return "".join(self)
    @property
    def string(self):
        """this gives us a property which represents the concatenated string"""
        return "".join(self)
 # use whatever suits you better, one or both

有了这个,我们可以做到:

class ThisWorksNow(object):
  def outer(self):
    acc = StringBuilder()
    def inner(msg):
      acc.append(msg)
    inner("Why doesn't")
    inner(" this work?")
    print acc
    print acc.string # depending on what you take above
ThisWorksNow().outer()

编辑(追加):为什么global不起作用?

我们也可以使用global实现此目标,但有2个缺点。

  1. acc必须在我们使用它的两个地方全局化

    class WhyDoesntThisWork(object):
      def outer(self):
        global acc
        acc = ''
        def inner(msg):
          global acc
          acc = acc + msg
    

    此时我们将acc次出现“提升”为“global”级别。

  2. acc可以从外面修改。

    如果我们在其他地方global acc,或者我们在模块级别使用acc,我们的流程可能会被篡改。应该避免这种情况。

答案 1 :(得分:0)

您可以创建一个如下所示的帮助器闭包类,以将变量显式标记为闭包。不确定这样的实用程序是否已经存在。

class closure:
    def __init__(self, val = None):
        self.val = val
    def __call__(self, val = None):
      if val:
        self.val = val
      return self.val
class WhyDoesntThisWork(object):
  def outer(self):
    acc = closure('')
    def inner(msg):
      acc(acc() + msg)
    inner("Why doesn't")
    inner(" this work?")
    print acc
WhyDoesntThisWork().outer()