#https://www.datacamp.com/community/tutorials/decorators-python def basicdeco(func): '''A basic wrapper that just adds things ''' def wrapper(): print('Some code inside the wrapper runs here') return func() #function has to run unless you want to run something like ()() return wrapper def deco1(func): '''A wrapper that reads args and kwargs and does nothing with them #print('before wrapper 2: runs on definition') Note that placing things before the wrapper will case them to run before the decorated function call because they must run to wrap the function ''' def wrapper(*args, **kwargs): print("This is decorated and took arguments (args, kwargs)") print(args, kwargs) print("but didn't pass them to the wrapped function") return func() return wrapper def deco2(func): '''A wrapper that reads args and kwargs and passes them to the function #print('before wrapper 2: runs on definition') Note that placing things before the wrapper will case them to run before the decorated function call because they must run to wrap the function Any positional arguments from the function are passed along with args. ''' def wrapper(*args, **kwargs): print("This is decorated and took arguments (args, kwargs)") print(args, kwargs) print("but didn't pass them to the wrapped function") return func(*args, **kwargs) return wrapper def deco3(*args, **kwargs): '''This time it is the *decorator* that takes arguments, then passes them to the decorated function. You have to write a decorator *within* a decorator print('whatever') #would also run on wrap Remember that wrappers can access variables outside their scope, but not vice versa, so the args and kwargs can filter down. ''' def insidedeco(func): print('These are the decorator args, kwargs:', args, kwargs)# prints on wrap def wrapper(*args1, **kwargs1): print("This is the decorated nested wrapper") print("Decorator variables", args, kwargs) print("Inner wrapper variables", args1, kwargs1) return func(*args1, **kwargs1) return wrapper return insidedeco def practical_example(func): '''A function counter wrapper''' def wrapper(*args, **kwargs): wrapper.count+=1 return func(*args, **kwargs) wrapper.count=0 #wrapper must be assigned first! That's why this is at the bottom. return wrapper @basicdeco def testme0(): print('@basic deco test function') print('Undecorated function takes no arguments') print(30*'_') @deco1 def testme1(): print('@deco1 test function') print(30*'_') @deco2 def testme2(positional): print('@deco 2 test function') print('positional variable', positional) print(30*'_') @deco3(deco3var="bollocks") def testme3(positional, *args, **kwargs): print('@deco3 test function') print('positional', positional) print('args, kwargs from decorated function:', args, kwargs) print(30*'_') @practical_example def countme(): '''see https://stackoverflow.com/questions/44968004/python-decorators-count-function-call for how the counting variable works ''' print('I count for something:', countme.count) print(30*'_') @practical_example def second_count(): print('I also count as a separate object', second_count.count) print(30*'_') def wo_decorator_syntax(): '''Just to show how it works without syntax''' print('Manually wrapped') print(30*'_') if __name__ == '__main__': print('__main__ starts running here___') print(30*'_') #decorator without shorthand basicdeco(wo_decorator_syntax)() #decorators in action testme0() testme1(name='PassedVariable') testme2("myPositionalVariable") testme3("positionalVar", name=37) for i in range(10): countme() for i in range(5): second_count()