Python Decorators — A beginners guide
Python decorators are a super powerful way to apply generic logic to functions by wrapping them in another function. This makes them perfect for applying generic logic to functions, Django views (like custom authentication) and a bunch of other stuff.
Python achieves this through some currying. Currying is where functions are the arguments and the return type. This means modifications can be made to how those arguments are passed, parsed or the order of execution.
A simple example would be where decorator prints the arguments of the function it’s called on.
def printargs(original_function):
# Original is the reference to the function
print(original_function)
print(type(original_function))
def new_function(*args, **kwargs):
#pass arguments to the other
print("Args; ", args)
print("KWARGS;", kwargs)
original_function(*args, **kwargs)return new_function
@printargs
def hello(*args, **kwargs):
print (args, kwargs)
hello("Earth", thing="NEW")
This will output
<function hello at 0x102627048>
<class 'function'>
Args; ('Earth',)
KWARGS; {'thing': 'NEW'}
('Earth',) {'thing': 'NEW'}
From the output it’s clear that the decorator is executed right before the original function.
A slightly more complicated use is for validation of arguments. Here i’m using it to validate that a passed argument is a dictionary and has at least “n” elements in it. This is good because it exposes the other method of making a decorator; with a class.
def validateDictionary(original_function):
def new_function(dictionary, *args, **kwargs):
if isinstance(dictionary, dict) == True:
print('Is a dictionary')
return original_function(dictionary, *args, **kwargs) else:
print("Not a dictionary")
return [] return new_function
class validLengthDictionary(object):
def __init__(self,length):
self.length = length def __call__(self, original_function):
def new_function( dictionary, *args, **kwargs): if len(dictionary) >= self.length:
print( len(dictionary), "Length")
return original_function(dictionary, *args, **kwargs) else:
print("Not long enough")
return original_function({}, *args, **kwargs) return new_function@validateDictionary
@validLengthDictionary(2)
def getKeys(dictionary, *args, **kwargs):
return list( dictionary.keys() )
These decorators will verify that the dictionaries are valid, and pass on a different dictionary if they’re invalid. It’s important to remember to add the return function or your function execution won’t reach the top. And that’s how to parse arguments with decorators, and pass arguments to decorators. This can increase your code reusability and save you from repeating the same code everywhere!