views:

170

answers:

5

I'm writing a function to traverse the user's file system and create a tree representing that directory (the tree is really a TreeView widget in Tkinter, but that's functionally a tree).

The best way I can think of doing this is recursion. However, one of my cases in the function requires me to know if it is the "original" function call, in which case the files have no parent node, or if it is a "recursive" function call, i.e. a call that has been made by the function itself, so that I can give those files an appropriate parent node.

Is there any way in Python to ask a function, "hey, are you recursive?" or "hey, where were you called from?"

+7  A: 

Pretty much the same as in every other language - in your case, you pass a reference to the parent and check if it is None. If so, you create a proper parent node.

Jim Brissom
That should work... thanks a lot!
Rafe Kettler
+1  A: 

It should be easy and common, to include a reference to the parent or some level information along with the call to the recursion.

One other way (which I wouldn't prefer, though) is to use pythons inspect module, which lets you inspect e.g. the call stack. An example:

#!/usr/bin/env python

import inspect

def whocalled():
    return inspect.stack()[2][3]

def fib(n):
    print n, whocalled()
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

if __name__ == '__main__':
    fib(4)

Would print:

4 <module>
3 fib
2 fib
1 fib
0 fib
1 fib
2 fib
1 fib
0 fib
The MYYN
+4  A: 

one of my cases in the function requires me to know if it is the "original" function call, in which case the files have no parent node

This seems like a strange case of too much work for the specific function. You have to construct the tree - why do you need to know where it's attached? Why not just construct the nodes you're responsible for and return them?

def make_tree(path):
    return [
        make_tree(os.path.join(path, element))
        for element in get_elements(path)]

And walk the tree again when you receive it?

If you really want to integrate it, just pass the parent:

def make_tree(path, parent_node = None):
    new_node = Node(...)
    for ....:
        make_tree(path+..., new_node)

    if parent_node is not None:
        parent_node.add(new_node)
    else:
        .....
viraptor
This is so brilliant. I didn't even think of using list comprehensions. This came at exactly the right time because I was just about ready to scrap the whole function since I felt like I was making a mess of things.
Rafe Kettler
Very intuitive. This is a great solution to the question.
Sean
I've been trying to use this list comprehension, but all it gets me is a bunch of nested empty lists. How could I get it to return the tree?
Rafe Kettler
@Rafe: You'll have to add the information you want to include - right now it does create lists only. Try returning `(path, make_tree(...))` instead of `make_tree(...)` itself - you'll notice how it works.
viraptor
@Viraptor: thanks mucho
Rafe Kettler
+1  A: 

You can access the memory stack through the inspectmodule.

import inspect

def whoami():
    '''Returns the function name of the caller.'''

    return inspect.stack()[1][3] #Stack data for the name of the function

def caller():
    '''Returns the caller of a function.'''

    return inspect.stack()[2][3] #Stack data for the name of whatever calls this

caller = caller()

You'll get an index out of range error if you attempt to call it from __main__.

when the caller of the function != whoami() -> no longer recursing.
Sean
+1  A: 

I really wonder why you make this so complicated. You can simply split the function in a recursive and a not recursive part! Here is a simple example:

def countdown(n):
    " original function "
    print "Ok, start the countdown (not recursive)"

    msg = "We are at"

    def recursive( x ):
        " recursive part "
        if x >= 0:
            print msg, x, "(recursive)"
            return recursive(x-1)

    return recursive(n)

countdown(10)

In reality you don't even need many arguments to the recursive function because it is a closure and can use anything you defined in it's namespace.

THC4k