10

我希望这个小片段能打印出“为什么这不起作用?” 有人可以帮我理解为什么这不能按我的预期工作吗?如果这很重要,我正在使用 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.
4

2 回答 2

9

I don't know why so many comments above contain the correct answer and no one dared to write an actual answer, so I'll do it hereby.

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()

What is the difference?

Assigning a name to an object which is in the closure doesn't work in Python 2.x, because the Py3 keyword nonlocal is missing, so we have to find a workaround.

If we have to keep the name-to-object-binding constant, we have to change something else. In this case, it is the object, to which we add the content to be added.

The print line is not very elegant; maybe an object which prints its contents concatenated might be more suitable.

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

With this, we can do that:

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()

Edit (append): Why does global not work?

We could achieve this with global as well, with 2 downsides.

  1. acc would have to be made global on both places we use it

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

    Hereby we "lift" both acc occurrences to "global" level.

  2. acc could be modified from outside.

    If we do global acc somewhere else, or we use acc on module level, our process can be tampered with. This should be avoided.

于 2012-06-22T22:19:20.583 回答
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()
于 2013-05-14T15:32:54.690 回答