From be5b3210c846ac6c30967b85eb0600141b5ec4dc Mon Sep 17 00:00:00 2001 From: Kashirigi Date: Fri, 16 Aug 2019 15:00:41 -0700 Subject: [PATCH] A complete explanation of decorators --- Python/Decorators/Decorators.py | 29 ----- Python/Decorators/decoCompleteExample.py | 123 ++++++++++++++++++++++ Python/Decorators/decoratortest.py | 18 +++- Python/Decorators/successful_decorator.py | 31 ------ 4 files changed, 139 insertions(+), 62 deletions(-) delete mode 100644 Python/Decorators/Decorators.py create mode 100644 Python/Decorators/decoCompleteExample.py delete mode 100644 Python/Decorators/successful_decorator.py diff --git a/Python/Decorators/Decorators.py b/Python/Decorators/Decorators.py deleted file mode 100644 index 3ff1fce..0000000 --- a/Python/Decorators/Decorators.py +++ /dev/null @@ -1,29 +0,0 @@ -def deco(func): - def wrapper(*args, **kwargs): - func(*args, **kwargs) - wrapper.count+=1 - wrapper.count=0 - return wrapper - -def deco2(func): - def wrapper(*args, **kwargs): - print(args) - def fname(*args): - print('args',args) - - return fname - -@deco2 -def fname(arg): - print(arg) - - -@deco -def main(arg): - print(arg) - -for item in range(3): - main(item) - print('count', main.count) - -fname('test') diff --git a/Python/Decorators/decoCompleteExample.py b/Python/Decorators/decoCompleteExample.py new file mode 100644 index 0000000..bf50990 --- /dev/null +++ b/Python/Decorators/decoCompleteExample.py @@ -0,0 +1,123 @@ +#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() diff --git a/Python/Decorators/decoratortest.py b/Python/Decorators/decoratortest.py index 0b2fd07..fc87f11 100644 --- a/Python/Decorators/decoratortest.py +++ b/Python/Decorators/decoratortest.py @@ -1,9 +1,23 @@ from random import randint +''' +How a decorator works: +1. Create the decorator function. This is what @whatever is. It takes a function as input. It *can* also take parameters as per: +https://www.geeksforgeeks.org/decorators-with-parameters-in-python/ + +2. Inside that function is a wrapper function. It will take the same number of parameters as the decorated function. + +3. Anything inside the wrapper function is defined, not run. + +4. The function call with parameters passed through is called. Then the wrapped function is returned. + +''' #this is the decorator def decor(func): #takes a function as an argument - def x(z): # z = arguments passed from function that was decorated + def x(z): # z = arguments passed from function that was decorated. Note that the number of arguments must be the same. + ''' Decorator function wrapper must take the same number of arguments or it will fail + ''' print('now with decoration') #extra shit from the decorator - func(z) #run that function + func(z) #run that function WHEN CALLED ON. Not now, because it is inside a function definition. return x #return the decorator function @decor #invoke the decorator diff --git a/Python/Decorators/successful_decorator.py b/Python/Decorators/successful_decorator.py deleted file mode 100644 index 9e8fe4f..0000000 --- a/Python/Decorators/successful_decorator.py +++ /dev/null @@ -1,31 +0,0 @@ -#import numpy as np -#import time -import math -from math import tan as qe - -def deco(func): - def wrapper(y): - ang = y *2 *math.pi/360 - return func(ang) - return wrapper #You need two returns! So confusing! - - -def test(number): - return number - -@deco -def test2(number): - return number - - -print('undecorated') -print(test(45)) -print('decorated') -print(test2(45)) -print('math.tan') -print(qe(45)) -print('math.tan decorated') -test3=deco(qe) -print(type(test3)) -print(test3(45)) -print(math.tan(45))