Python closure + global strangeness

python

Solution

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.

`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.

`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.

Problem

I expected this little snippet to print "Why doesn't this work?" Can someone help me understand why this doesn't work as I expect? I'm using Python 2.6, if this matters. ``` 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() ``` - If I include the `global` statement I get a `NameError: global name 'acc' is not defined `. - If I don't include the `global` statement I get a `UnboundLocalError: local variable 'acc' referenced before assignment`.

Original source