Есть ли способ узнать, использую ли я рекурсию в Python? - PullRequest
1 голос
/ 22 сентября 2010

Я пишу функцию для обхода файловой системы пользователя и создания дерева, представляющего этот каталог (дерево действительно является виджетом TreeView в Tkinter, но это функционально дерево).

Лучший способ, которым яможет думать, что делать это рекурсия.Тем не менее, один из моих случаев в функции требует, чтобы я знал, является ли это «исходным» вызовом функции, и в этом случае файлы не имеют родительского узла, или это рекурсивный вызов функции, то есть вызов, который былсделанный самой функцией, так что я могу дать этим файлам соответствующий родительский узел.

Есть ли в Python способ задать функции: "эй, вы рекурсивны?"или "эй, откуда тебя звали?"

Ответы [ 5 ]

7 голосов
/ 22 сентября 2010

Практически так же, как и на любом другом языке, - в вашем случае вы передаете ссылку родителю и проверяете, есть ли она.Если это так, вы создаете правильный родительский узел.

4 голосов
/ 22 сентября 2010

один из моих случаев в функции требует, чтобы я знал, является ли это "исходным" вызовом функции, и в этом случае файлы не имеют родительского узла

Это кажется страннымслучай слишком большой работы для конкретной функции.Вы должны построить дерево - зачем вам знать, где оно прикреплено?Почему бы просто не создать узлы, за которые вы отвечаете, и вернуть их?

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

И снова пройтись по дереву, когда вы его получите?

Если вы действительно хотите интегрировать его, просто передайтеродитель:

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:
        .....
1 голос
/ 22 сентября 2010

Мне действительно интересно, почему вы делаете это так сложно.Вы можете просто разделить функцию на рекурсивную и нерекурсивную части!Вот простой пример:

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)

В действительности вам даже не нужно много аргументов для функции recursive, потому что она является замыканием и может использовать все, что вы определили в ее пространстве имен.

1 голос
/ 22 сентября 2010

Вы можете получить доступ к стеку памяти через модуль inspect.

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

Вы получите ошибку индекса вне диапазона, если попытаетесь вызвать его из __main__.

when the caller of the function != whoami() -> no longer recursing.
1 голос
/ 22 сентября 2010

Это должно быть легко и обычно включать ссылку на родителя или некоторую информацию об уровне вместе с вызовом рекурсии.

Еще один способ (хотя я бы не предпочел) использовать модуль питонов inspect, который позволяет вам проверять, например, стек вызовов. Пример:

#!/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)

будет печатать:

4 <module>
3 fib
2 fib
1 fib
0 fib
1 fib
2 fib
1 fib
0 fib
...