DEV Community

Ugbem Job
Ugbem Job

Posted on

Day 5: Advanced Python: Decorators

What I learnt

  • Decorators

Decorators are a way to modify or extend the functionality of a class,method or function without changing the source code. Some implementations where decorators are commonly used are logging, caching, authentication and authorization.
Decorators are usually written as functions that takes another function as argument and return a modified version of the original function.

Example of decorators

def decorator_func(func):
    def wrapper_func():
        print(f'This executes before {func.__name__}')
         return func()
    return wrapper_func

@decorator_func
def greeting():
    print('You\'re a great learner!')

greeting()

# Another common way decorators are being written is shown below
def greeting():
    print("You're doing well")

greet = decorator_func(greeting)
greet()
Enter fullscreen mode Exit fullscreen mode

Decorating our functions allows us to easily add extra functionalities to existing function by adding that functionality inside of our wrapper.

Decorators with Arguments

def decorator_func(func):
    def wrapper_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper_func

@decorator_func
def user():
    print('User function called')

user()

@decorator_func
def user_profile(username, email, age):
    print('This is your profile info: \nUsername:{} \nEmail:{} \nAge:{}'.format(username, email, age))

user_profile('Jobizil', 'jobizil@email.com', '22')
Enter fullscreen mode Exit fullscreen mode

Using classes as decorators

class decorator_class(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, \*\*kwargs):
        print(f'This executes before {self.func.__name__}')
    return self.func(*args, **kwargs)

@decorator_class
def user():
    print('User function called')
user()

@decorator_class
def user_profile(username, email, age):
    print('This is your profile info: \nUsername:{} \nEmail:{} \nAge:{}'.format(username, email, age))

user_profile('Jobizil', 'jobizil@email.com', '22')

Enter fullscreen mode Exit fullscreen mode

Other Practical examples with Decorators

  • 1. Using decorators for logging
from functools import wraps
import time

def logger(func):
    import logging
    logging.basicConfig(filename='{}.log'.format(
    func.**name**), level=logging.INFO)

    @wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f'Ran with args:{args}, and kwargs:{kwargs}')
        return func(*args, **kwargs)
    return wrapper

@logger
def user_profile(username, email, age):
    time.sleep(2)
    print('This is your profile info: \nUsername:{} \nEmail:{} \nAge:{}'.format( username, email, age))

user_profile('Jobizil', 'jobizil@email.com', '22')

Enter fullscreen mode Exit fullscreen mode
  • 2. Using decorators as timers to determine how long a function took to complete execution
def timer(func):
    from time import time

    @wraps(func)
    def wrapper(*args, **kwargs):
        t1 = time()
        result = func(*args, **kwargs)
        t2 = time() - t1
        print(f'{func.__name__} took {t2}s to complete its execution.')
        return result
    return wrapper

@timer

def user_profile(username, email, age):
    time.sleep(2)
    print('This is your profile info: \nUsername:{} \nEmail:{} \nAge:{}'.format(username, email, age))
Enter fullscreen mode Exit fullscreen mode

To stack multiple decorators, we need to import the wraps decorator from the functools module as this helps preserve the original names of the functions.

@timer
@logger
def user_profile(username, email, age):
    time.sleep(2)
    print('This is your profile info: \nUsername:{} \nEmail:{} \nAge:{}'.format(username, email, age))

user_profile('Jobizil', 'jobizil@email.com', '22')

Enter fullscreen mode Exit fullscreen mode

Exercises

#  Create an @authenticated decorator that only allows the function to run is user1 has 'valid' set to True:
user = {
    'name': 'Sorna',
    'valid': False
}


def authenticated(fn):
    # Your code here

@authenticated
def message_friends(user):
    print('message has been sent')


message_friends(user)
Enter fullscreen mode Exit fullscreen mode

Resources

Conclusion

I learnt about decorators and how they can be used to modify the functionality of a function without changing the source code. I also learnt how to write decorators and how to use them to add extra functionalities to existing functions.

Top comments (0)