tags:

views:

74

answers:

3

Hi to all,

I have the following function that walks a nested tree and prints the result

def walk_tree(tree):   
    def read_node(node):
        print node
        for n in node['subnodes']:
            read_node(n)
    read_node(tree)

If I want to return a txt with the data collected from walking the tree, thought that the following would have worked:

def walk_tree(tree):
    txt = ''  
    def read_node(node):
        txt += node
        for n in node['subnodes']:
            read_node(n)
    read_node(tree)

But txt doesn't seem to be in read_node's scope. Any suggestion? Thanks

A: 

You could try:

def walk_tree(tree):
    txt = ''  # (1)
    def read_node(node, txt=txt):
        txt += node # (2)
        for n in node['subnodes']:
            read_node(n, txt)
    read_node(tree)

This binds walk_trees value of txt to the default value in read_node, which should persist (if my grasp of Python theory is correct).

detly
This won't work because strings are immutable in Python, so `txt += node` will create a new string instead of changing the existing one.
interjay
Ah, of course. My bad.
detly
+5  A: 

txt is accessible in read_node, I think it's just some problem with += and that txt is not in local scope in read_node.

>>> def a():
...  x = ""
...  def b():
...   x += "X"
...  b()
...  print x
... 
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in a
  File "<stdin>", line 4, in b
UnboundLocalError: local variable 'x' referenced before assignment
>>> 
>>> 
>>> def a():
...  x = []
...  def b():
...   x.append("X")
...  b()
...  print "".join(x)
... 
>>> a()
X

You should use list and "".join(...) instead of str += ... anyways.

Messa
+1  A: 

In Python you cannot rebind variables of an outer scope (of walk_tree in your example).

So this will fail:

def a():
    def b():
        txt += "b" # !!!

        print txt

    txt = "mytext"
    b()

a()

but this will work:

def a():
    def b():
        # removed assignment: txt += "b"

        print txt

    txt = "mytext"
    b()

a()

So you may want to avoid rebinding:

def a():
    def b():
        obj["key"] = "newtext"

    obj = {"key" : "mytext"}
    b()

a()
AndiDog