Understanding Python Decorators

As in Python, a function can return another function then we can take it’s benefit.

def our_parent_function(): print("Hello from parent function") def our_child_function(): print("Hello from child function") return our_child_function # returning child functionchild_function = our_parent_function()child_function() # calling child function>>> Hello from parent function>>> Hello from child functionHere, we are returning the reference of our_child_function, storing in child_function and calling it.

Interesting, No?Our first decoratorLet, we want to print our country’s name along with our name but we won’t change our describe_myself function.

def our_first_decorator(func): def wrapper(): # it can be any named, but according to convention "wrapper" matches better # do something here func("Mr.

Coder") # do something here print("Bangladesh") # do something here return wrapperdef describe_myself(name): print(name)describe_myself = our_first_decorator(describe_myself)describe_myself()>>> Mr.

Coder>>> BangladeshHere, our_first_decorator takes func as argument, modifies its behavior and returns a new function.

We are assigning the modified functions reference to describe_myself variable and calling through it.

Yes, this is our first decorator!.A decorator wraps a function by modifying its behavior.

We’ve done the same thing in the previous code.

Polish our first decoratorUsing our_first_decorator is not looking awesome yet.

We can use it in smarter way by using @ symbol (sometimes, @ is called pie-decorator) .

The following block of code will do the same as previous one.

def our_first_decorator(func): def wrapper(): func("Mr.

Coder") print("Bangladesh") return wrapper@our_first_decorator # nice, no?def describe_myself(name): print(name)describe_myself()Here we’ve hard coded “Bangladesh” in our wrapper function.

We can pass arguments for the wrapper too!def our_first_decorator(func): def wrapper(country): # updated func("Mr.

Coder") print(country) # updated return wrapper@our_first_decoratordef describe_myself(name): print(name)describe_myself("Bangladesh") # updatedIt looks nicer now!Introspection our decoratorLet’s introspect our function and decorator:def our_first_decorator(func): def wrapper(country): func("Mr.

Coder") print(country) return wrapper@our_first_decoratordef describe_myself(name): print(name)print(our_first_decorator)print(our_first_decorator.

__name__)>>> <function our_first_decorator at 0x7f6b039300d0>>>> our_first_decorator # looks fineprint(describe_myself)print(describe_myself.

__name__)>>> <function our_first_decorator.

<locals>.

wrapper at 0x7f6b03930268>>>> wrapper (why wrapper? shouldn't it be describe_myself?)Wait, for print(describe_myself.

__name__) it shows that name of describe_myself function is wrapper!.Shouldn’t it be describe_myself?However, after being decorated, describe_myself has gotten confused about its identity.

It reports of being the wrapper inner function of our_first_decorator.

Though this information is technically not wrong but not very useful information.

We can solve this issue by using @functools.

wraps decorator.

A custom decorator should use this decorator to preserve its information.

from functools import wrapsdef our_first_decorator(func): @wraps(func) # see this update def wrapper(country): func("Mr.

Coder") print(country) return wrapper@our_first_decoratordef describe_myself(name): print(name)print(describe_myself)print(describe_myself.

__name__)>>> <function describe_myself at 0x7f8b7f8391e0>>>> describe_myself # now it's fine!It looks fine now!In this brief discussion, we have completed some basics of python decorator and developed our first python decorator! :).

. More details

Leave a Reply