Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Python method check if it has been called from within itself?

Let's say I have a Python function f and fhelp. fhelp is designed to call itself recursively. f should not be called recursively. Is there a way for f to determine if it has been called recursively?

like image 764
Joseph Turian Avatar asked Oct 26 '11 08:10

Joseph Turian


People also ask

How do you check if a method has been called in Python?

Python callable() Function The callable() function returns True if the specified object is callable, otherwise it returns False.

Can you call a function within itself in Python?

Python also accepts function recursion, which means a defined function can call itself. Recursion is a common mathematical and programming concept. It means that a function calls itself. This has the benefit of meaning that you can loop through data to reach a result.

What is it called when a method calls itself?

Recursion is the process of defining something in terms of itself. Recursion is sometimes called circular definition. A function that calls itself is said to be recursive. Example.

Can a method call itself?

Yes, you can. It is called a recursive function.


2 Answers

Use the traceback module for this:

>>> import traceback
>>> def f(depth=0):
...     print depth, traceback.print_stack()
...     if depth < 2:
...         f(depth + 1)
...
>>> f()
0  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
 None
1  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in f
  File "<stdin>", line 2, in f
 None
2  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in f
  File "<stdin>", line 4, in f
  File "<stdin>", line 2, in f
 None

So, if any entry in the stack indicates that the code was called from f, the call was (in)directly recursive. The traceback.extract_stack method gives you an easy access to this data. The if len(l[2] ... statement in the example below simply counts the number of exact matches of the name of the function. To make it even prettier (thanks to agf for the idea), you could make it into a decorator:

>>> def norecurse(f):
...     def func(*args, **kwargs):
...         if len([l[2] for l in traceback.extract_stack() if l[2] == f.__name__]) > 0:
...             raise Exception('Recursed')
...         return f(*args, **kwargs)
...     return func
...
>>> @norecurse
... def foo(depth=0):
...     print depth
...     foo(depth + 1)
...
>>> foo()
0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in func
  File "<stdin>", line 4, in foo
  File "<stdin>", line 5, in func
Exception: Recursed
like image 75
jro Avatar answered Oct 25 '22 02:10

jro


You could use a flag set by a decorator:

def norecurse(func):
    func.called = False
    def f(*args, **kwargs):
        if func.called:
            print "Recursion!"
            # func.called = False # if you are going to continue execution
            raise Exception
        func.called = True
        result = func(*args, **kwargs)
        func.called = False
        return result
    return f

Then you can do

@norecurse
def f(some, arg, s):
    do_stuff()

and if f gets called again while it's running, called will be True and it will raise an exeption.

like image 43
agf Avatar answered Oct 25 '22 01:10

agf