If you don't want to rewrite your recursive function, you could also decorate it in a function that counts it using a counter of some sort. Example of an implementation:
UPDATE: I've changed the answer, but the old answer is kept at the end of the answer //UPDATE
Assume here that you have some recursion functions in some_module.py:
# From some_module.py
def factorial(x):
return factorial(x-1)*x if x > 1 else 1
def cumsum(x):
return cumsum(x-1) + x if x > 1 else 1
def loopBST(root):
# ... your code
And you want to apply the decorator to count how many recursions ran. Here, the code is performed inside some_function()
to show that you don't have to keep the count variable(s) in the global scope. (See comments) (Also, the recursive functions are still in global space)
# Main file:
from functools import wraps, partial
from collections import defaultdict
# import the two recursion functions from some_module.py
from some_module import cumsum, factorial, loopBST
def some_function():
global factorial, cumsum, loopBST
def counting_recursion(fn, counter):
@wraps(fn)
def wrapper(*args, **kwargs):
counter[fn.__name__] += 1
return fn(*args, **kwargs)
return wrapper
counters = defaultdict(int)
my_deco = partial(counting_recursion, counter=counters)
factorial = my_deco(factorial)
cumsum = my_deco(cumsum)
loopBST = my_deco(loopBST)
print(factorial(3))
# 6
print(cumsum(5))
# 15
factorial_count = counters[factorial.__name__]
cumsum_count = counters[cumsum.__name__]
loopBST_count = counters[loopBST.__name__] # Equals 0: were not called in my example
print('The "{}" function ran {} times'.format(factorial.__name__, factorial_count))
print('The "{}" function ran {} times'.format(cumsum.__name__, cumsum_count))
# The "factorial" function ran 3 times
# The "cumsum" function ran 5 times
A few modifications/variations:
Instead of using my_deco = partial(counting_recursion, counter=counters)
, the recursive functions could be decorated directly:
cumsum = counting_recursion(cumsum, counter=counters)
factorial = counting_recursion(factorial, counter=counters)
loopBST = counting_recursion(loopBST, counter=counters)
Instead of using fn.__name__
to identify the called function, the counting_recursion
-function could be rewritten as:
def counting_recursion(fn, counter):
@wraps(fn)
def wrapper(*args, **kwargs):
counter[wrapper] += 1
return fn(*args, **kwargs)
return wrapper
Then, to read the number from the counters
dictionary:
factorial_count = counters[factorial]
cumsum_count = counters[cumsum]
loopBST_count = counters[loopBST]
If you want to read more about wrapping functions, check out https://stackoverflow.com/a/25827070/1144382 and the docs on wraps
OLD EXAMPLE:
from functools import wraps, partial
class Counter:
def __init__(self, start_count=0):
self._counter = start_count
def __repr__(self):
return str(self._counter)
def count(self):
self._counter += 1
counter = Counter()
def counting_recursion(fn, counter):
@wraps(fn)
def wrapper(*args, **kwargs):
counter.count()
return fn(*args, **kwargs)
return wrapper
my_deco = partial(counting_recursion, counter=counter)
@my_deco
def factorial(x):
return factorial(x-1)*x if x > 1 else 1
print(factorial(3))
# 6
print('The {} function ran {} times'.format(factorial.__name__, counter))
# The factorial function ran 3 times
To implement this in your case, just make some counter and decorate your function:
@my_deco
def loopBST(root):
# ...
print(counter._counter)
# prints number of counts
Of course, you don't have to make a Counter
-class to call counter.count()
on, you could also have a dictionary of counters, e.g. counts[loopBST] += 1
or just an array with a single element count_list[0] += 1
. (see the code example at top of this answer) (The entire point is to "hide" the value in a reference that is not rewritten when the variable is reassigned, which is why just an integer count count += 1
won't work.)