from typing import Any, Callable def count_calls[T](func: Callable[..., T]) -> Callable[..., T]: count_calls.calls = 0 def wrapper(*args, **kwargs) -> T: init_calls = count_calls.calls count_calls.calls += 1 result = func(*args, **kwargs) wrapper.calls = count_calls.calls - init_calls return result return wrapper def f(x: int, y: int) -> int: if x % 2 == 0: return x // 2 else: return x + 2 * y - 1 def count_iterations(a: int, b: int) -> int: f_a(a, a, b) return f_a.calls - 1 @count_calls def f_a(init: int, a: int, b: int) -> None: if a < b: return return f_a(init, f(a, init), b) def execute_by(n: int): def wrapper(fn): def wrapped_fn(*args, **kwargs): for _ in range(0, n): fn(*args, **kwargs) return wrapped_fn return wrapped_fn return wrapper @execute_by(10) def hello_world(): print('hello world!') @execute_by(10) def print_ten_times(msg: str): print(msg) def execute_two_times(fn) -> Callable[..., Any]: def wrapper(*args, **kwargs): for _ in range(0, 2): fn(*args, **kwargs) return wrapper return wrapper @execute_two_times def test(msg: str): print(msg) if __name__ == '__main__': assert (i := count_iterations(7, 6)) == 3, i assert (i := count_iterations(3, 2)) == 4, i assert (i := count_iterations(13, 9)) == 18, i assert (i := count_iterations(13, 10)) == 8, i assert (i := count_iterations(3, 4)) == 0, i print_ten_times("hello world") test("hello")