Decorators in python - Part 1

Decorators in python is very useful in data hiding and performing pre and post operation using simple function object. Below is a example of decorator and how its working in python interpretor.

def sample_decorator(func):
    def wrapper(val):
        print("calling a function for pre-processing")
        return_value = func(val)
        print("calling post-processing function")
        return return_value
    return wrapper

def sample_function(val):
    print(f"hai from sample function {val}")

if __name__ == "__main__":
    sample_function = sample_decorator(sample_function)
    sample_function(10)

In python everything is object so what we are doing above is adding a syntatical sugar using a decorator that replaces the sample_function object with newly created sample_function object. We have created sample_function object which is passed to function which has another function in inner-scope this function object wrapper is returned. Effectively we have returned the function object and its states to caller. When calling this function object it can access its local variables and func object passed as parameter because these variables are part of the local stack of the wrapper object. Python uses a concept called closure which is a property that refers the variable of the outerscope function including func object passed to sample_decorator

Every time we cant able to write the code using wrapper function like above so effectively we add the syntatical sugar with the actual function itself so below code @sample_decorator.

@sample_decorator
def sample_function(val):
    print(f"hai from sample function {val}")

which expands the code like what we have done in the __main__.

The above code wont work for generic function for example what happens when I can call a function with two parameters or multiple parameters for that we can create a generic decorator like below extending the above code.

def sample_decorator(func):
    def wrapper(*args, **kwargs):
        print("calling a function for pre-processing")
        return_value = func(*args, **kwargs)
        print("calling post-processing function")
        return return_value
    return wrapper

@sample_decorator
def sample_function(param1, param2, param3):
    print(f"hai from sample function {param1}, {param2}, {param3}")

if __name__ == "__main__":
    sample_function(10, "hello world", param3 = [1,2,3])

So we have packed and unpacked the arguments while passing to decorators.

How can I use a decorator in practical, I can use to write my own logging function that acts as a decorator so this code below is logger function.

#Generator for Logging
def logging(function):
    import time
    @functools.wraps(function)
    def wrapper(*args, **kwargs): #decorator accepting the variable arguments and passing it to actual function used by decorator
        t_str = time.strftime("%a, %d %b %Y %I:%M:%S %p %Z", time.gmtime())
        return_value = function(*args, **kwargs)
        with open("logging.txt", "a+t") as f: #context manager that takes care of destroying the resource even when there is exception inside the context manager block
            name = function.__name__
            params = ""
            for i in [str(p) for p in args]:
                params = params + i + ", "
            for i in kwargs.values():
                params = params +  str(i) + ","        
            log_str = f"calling {name} with arguments{params}return value {return_value} at time - {t_str}\n"
            f.write(log_str)
        return return_value
    return wrapper

@logging
def sample_function(param1, param2, param3):
    print(f"hai from sample function {param1}, {param2}, {param3}")
    return str(param1) + str(param2) + str(param3)

if __name__ == "__main__":

    print(sample_function(10, "hello world", param3 = [1,2,3]))

Above logging function can be used as a decorator which will write the logs to the logging.txt and then calls the function capture its return value and put it in log file with time when the function executed.

Hope this article helps if you need more like this put a like or comment.

Next I will cover how to write a class decorator and decorator stacking.

Comments

Popular posts from this blog

Class Decorator practical uses - Part 2

Public key cryptogrphy - How certificate validation works using certificate chain