import itertools import functools from time import perf_counter, sleep import sys def do_twice(func): @functools.wraps(func) def wrapper(*args, **kwargs): for _ in itertools.repeat(None, 2): func(*args, **kwargs) return func(*args, **kwargs) return wrapper def timer(func): @functools.wraps(func) def wrapper_timer(*args, **kwargs): start_time = perf_counter() value = func(*args, **kwargs) end_time = perf_counter() run_time = end_time - start_time print(f'Finished {func.__name__!r} in {run_time:.4f} secs') return value return wrapper_timer def debug(func): """ Print the function signature and return the value """ @functools.wraps(func) def wrapper_debug(*args, **kwargs): args_repr = [repr(a) for a in args] kwargs_repr = [f'{k}={v!r}' for k, v in kwargs.items()] signature = ', '.join(args_repr + kwargs_repr) print(f'Calling {func.__name__}({signature})') value = func(*args, **kwargs) print(f'{func.__name__!r} returned {value!r}') return value return wrapper_debug def slow_down(func): @functools.wraps(func) def wrapper_slow_down(*args, **kwargs): sleep(1) return func(*args, **kwargs) return wrapper_slow_down def repeat(num: int): def decorator_repeat(func: callable): @functools.wraps(func) def wrapper_repeat(*args: list, **kwargs: dict): for _ in range(num): value = func(*args, **kwargs) return value return wrapper_repeat return decorator_repeat def repeatN(_func=None, *, num=2): def decorator_repeatN(func): @functools.wraps(func) def wrapper_repeatN(*args, **kwargs): for _ in range(num): value = func(*args, **kwargs) return value return wrapper_repeatN if _func is None: return decorator_repeatN else: return decorator_repeatN(_func) def repeat_partial(func=None, *, num=2): if func is None: return functools.partial(repeat_partial, num=num) @functools.wraps(func) def repeat_partial_wrapper(*args, **kwargs): for _ in range(num): value = func(*args, **kwargs) return value return repeat_partial_wrapper # def count_calls(func: callable): # @functools.wraps(func) # def wrapper_count_calls(*args, **kwargs): # wrapper_count_calls.num_calls += 1 # print(f'Call {wrapper_count_calls.num_calls} of {func.__name__!r}') # return func(*args, **kwargs) # wrapper_count_calls.num_calls = 0 # print(wrapper_count_calls.num_calls) # return wrapper_count_calls # def count_calls(func: callable): # num_calls = 0 # print(num_calls) # @functools.wraps(func) # def wrapper_count_calls(*args, **kwargs): # nonlocal num_calls # num_calls += 1 # print(f'Call {num_calls} of {func.__name__}') # return func(*args, **kwargs) # return wrapper_count_calls def count_calls(func=None, *, num=0): if func is None: return functools.partial(count_calls, num=num) @functools.wraps(func) def wrapper_count_calls(*args, **kwargs): wrapper_count_calls.num += 1 print(f'Call {wrapper_count_calls.num} of {func.__name__}') return func(*args, **kwargs) wrapper_count_calls.num = num return wrapper_count_calls class Counter(object): """docstring for Counter""" def __init__(self, func): print('start init') super(Counter, self).__init__() functools.update_wrapper(self, func) self.func = func self.num_calls = 0 print('finished init') def __call__(self, *args, **kwargs): print('start call') self.num_calls += 1 print(f'Call {self.num_calls} of {self.func.__name__!r}') print('finished call') return self.func(*args, **kwargs) # class slowDown(object): # """docstring for slowDown""" # def __init__(self, rate): # if callable(rate): # self.func = rate # self.rate = 1 # else: # self.rate = rate # def __get__(self, obj, type=None): # return functools.partial(self, obj) # def __call__(self, *args, **kwargs): # if not hasattr(self, 'func'): # self.func = args[0] # return self # sleep(self.rate) # self.func(*args, **kwargs) class slowDown(object): """docstring for Slow_Down""" def __init__(self, rate=1): print('init') self.rate = rate def __call__(self, func): @functools.wraps(func) def wrapper(*args, **kwargs): print('wrapper') sleep(self.rate) func(*args, **kwargs) print('call finished') print(self, func) return wrapper # class slowDown(object): # """docstring for slowDown""" # def __init__(self, rate): # if callable(rate): # self.func = rate # self.rate = 1 # print(f'no args, {locals()}, func={self.func}, rate={self.rate},\ # self={self}') # else: # self.rate = rate # print(f'args set, rate={self.rate}') # def __get__(self, obj, type=None): # print(f'get called, self={self}, obj={obj}, type={type}') # return functools.partial(self, obj) # def __call__(self, *args, **kwargs): # print(f'call called, rate={self.rate}, args={args}' # f', kwargs={kwargs} ,self={self}') # print(f'locals = {locals()}') # if not hasattr(self, 'func'): # self.func = args[0] # print(f'args set, setting self.func to {self.func}') # return self # sleep(self.rate) # self.func(*args, **kwargs)