Вот еще один ответ с использованием декоратора. Преимущество использования декоратора состоит в том, что базовая функция fib
не нуждается в изменении. Это означает, что код total_count
и несколько возвращаемых значений могут быть отброшены с вашей первоначальной попытки -
@counter(model = fib_result)
def fib(n = 0):
if n < 2:
return n
else:
return fib(n - 1) + fib(n - 2)
Наш декоратор counter
принимает model
, чтобы мы могли реагировать на поведение декорированной функции. Наш украшенный fib
вернет fib_result
, например { result: ?, count: ? }
. Это означает, что нам также необходимо обработать fib(?) + fib(?)
, поэтому мы также определили __add__
-
class fib_result:
def __init__(self, result, count = 0):
if isinstance(result, fib_result):
self.result = result.result
self.count = result.count + count
else:
self.result = result
self.count = count
def __add__(a, b):
return fib_result(a.result + b.result, a.count + b.count)
Как видите, этот fib_result
указывает c на подсчет fib
вызовов. Для подсчета других рекурсивных функций, которые возвращают результаты других типов, может потребоваться другая модель.
Теперь осталось только определить наш обобщенный c counter
декоратор. Наш декоратор принимает аргументы и возвращает новый декоратор lambda f: ...
, который просто захватывает переданные аргументы lambda *args: ...
и создает новый model
с результатом f(*args)
и базовым счетом 1
-
def counter(model = tuple):
return lambda f: lambda *args: model(f(*args), 1)
Вот как работает вся программа -
r = fib(4)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 3, recurs: 9
r = fib(10)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 55, recurs: 177