Python: Decorators Part I
Decorators attach additional responsibility to the object dynamically. A decorator takes other function as an argument which it processes and returns that function or any other callable object.
So how the decorator looks like
@clean_strings
def get_full_name(first_name, middle_name, last_name):
return first_name + middle_name + last_name
In above code snippet we have a decorator clean_strings, this can also be written in this way.
def get_full_name(first_name, middle_name, last_name):
return first_name + middle_name + last_name
get_full_name = clean_strings(get_full_name)
There are one two things we will talk about decorator after understanding.
- Variable Scope
- Closure
Variable Scope
In every language, the variable has a scope where they are accessible and where not. So here we will talk about the local scope and global scope lets jump right into the code to see
def show(a):
print(a)
print(b)
>>> show(10)
10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in show
NameError: name 'b' is not defined
We got the error as b is not defined in the scope
b = 101
def show(a):
print(a)
print(b)
>>> show(10)
10
101
here its work fine as b is defined in the global scope, which can be accessed from within the function. let see another code snippet.
b = 101
def show(a):
print(a)
print(b)
b =190
>>> show(10)
10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in show
UnboundLocalError: local variable 'b' referenced before assignment
here we face error as code interpret b as local variable of the function which being accessed before declaring as it is defined in the scope of the function. To treat b as global variable despite the assignment in function we can use global declaration.
b = 101
def show(a):
global b
print(a)
print(b)
global b =190
>>> show(10)
10
101
>>> b
190
Closure
Closure are the function which have access to the non global variables referenced in the body of function.
Figure from Fluent Python Book, chapter 7
In Python3 nonlocal was introduced which allows assigning the variable inside the scope.
Consider an avg function to compute the mean of an ever-increasing series of values; for example, the average closing price of a commodity over its entire history. Every day a new price is added, and the average is computed taking into account all prices so far.
def avg_series():
count = 0
total = 0
def averager():
nonlocal count, total
count += 1
total += new_value
return total / count
return averager
The need of using nonlocal here is that if we don't, Python assumes that count and total are the local variable of averager method, which will break our logic.
Code example is taken from Fluent Python Book.
Now lets build a decorator that can logs the runtime for the function.
import time
def log_time(func):
def clocked(*args):
start_time = time.time()
result = func(*args)
elapsed_time = time.time() - start_time
print("Elapsed time: {}".format(elapsed_time))
return result
return clocked
There are also built-in decorators in Python Standard Library which we will discuss in the next blog post, so stay tuned till then cheers.