Published: Aug. 5, 2019 by lukemakin |  estimated reading time: 8 minutes
Python custom decorators explained
Python decorators basically allow us to define a new function inside an already existing one in order to extend it's functionality, but without changing its structure. This feature provides a better way of writing code thanks to the usage of reusable blocks that are some kind of wrappers for a function (just like wrap paper for items that we want to give as a present). 

At first the concept may seem a little bit confusing because... it actually is. Adding a function inside another function? Passing functions as arguments? Returning a function from a function? All of this comes with the decorators, but if you never used them - it may be hard for you to understand them, so we''ll go through two examples that hopefully will clarify how they work.

The first example is very simple - we extend favourite_number function so before its launch it will print "hello", and after "goodbye".

import random

def simple_decorator(func): <--- here we are passing function we want to wrap
def say_hello_and_goodbye(x,y):
print("hello")
func(x,y) <--- wrapping our function
print("goodbye")
return say_hello_and_goodbye <--- returning a function inside a function

@simple_decorator
def favourite_number(x,y):
num = random.randint(x,y)
print(f"my favourite number is {num}")

favourite_number(0,10)


'''
hello
my favourite number is 4
goodbye
'''

Test this out for your own:


The second example will be related to withdrawing or depositing funds to a company account. In order to do this - you must have permissions, and checking this is something that our decorator will handle. Let's take a look:

permission_list = ['John', 'Chris', 'Jane']
funds = 10000


def new_decorator(func):
def check_permission(user, amount):
if user in permission_list:
func(user, amount)
else:
print("You don't have the permission")
print(f"Company funds: {funds}")

return check_permission


@new_decorator
def withdraw_funds(user, amount):
global funds
if funds > amount:
funds -= amount
print(f"{user} has withdrawn {amount}")
else:
print("not enough funds")
print(f"requested amount to withdraw: {amount}")


@new_decorator
def deposit_funds(user, amount):
global funds
funds += amount
print(f"{user} has deposit {amount}")

This time we used our decorator to extend both functions - 'withdraw_funds' and 'depsoit_funds' proving the reusability of decorators. Lets take a look at 3 scenarios:


deposit_funds('John', 200)
'''
Output:
John has deposit 200
Company funds: 10200
'''

withdraw_funds("Jane", 10000000)
'''
Output:
not enough funds
requested amount to withdraw: 10000000
Company funds: 10000
'''

withdraw_funds("Bill", 5000)
'''
Output:
You don't have the permission
Company funds: 10000
'''

In the first scenario John is in the permission list so he can deposit any amount of money. In our case he increased the company funds by $200. In the second example - Jane wanted to withdraw too big amount of money, so she got a rejection handled by withdraw_funds function itself. The wrapper function additionally confirmed that the amount of company funds hasn't changed. In the third scenario we have a situation that a user without permissions is trying to withdraw funds. He get's a notification from the wrapper function on the current amount of the funds.


That is it! Hopefully you get the idea behind the decorators. Please feel free to practice on your own using / modifying the examples above.

 
Extras
To view additional content login or create a free account
Categories:
Share your thoughts

johnny_j
1 year, 4 months ago
followed this step by step and I thin I get it know...
Signup to the newsletter
To get the latest updates from pyplane
© copyright 2019 pyplane.com