Python Decorator
When programming a few things that share common behaviours, we tend to wrap those common behaviours separately as functions so they can be called again and again without re-implementation. For example, in Python Flask which defines the http GET/POST interface, we need to receive a HTTP request and do something accordingly and finally return a HTTP response. The 'do something' is different for different tasks, but the 'receive request' and 'return reponse' are the same for every HTTP handling. To avoid programming the 'receive http' and 'return response' again and again, we can have them as two functions:
def receive_request():
... do stuff
def return_reponse():
... do stuff
And then for every specific http handling, we can have:
def handle_homepage_request():
receive_request()
do_something()
return_response()
Similar for other page handling as well. It just needs to call receive_request() first to get the http request data, and then do something accordingly, and finally return_response() to return the reponse. e.g.
def handle_download_request():
receive_request()
... do something
return_response()
def handle_login():
receive_request()
... do something
return_response()
etc.
Looks handy?? People are still sick of writing the two lines 'receive_request()' and 'return_response()' again and again. It becomes worse when you need to do more than the two lines. Imagine in other scenarios other than http, it probably has more common behaviours to do repeatly.
Therefore, people invent the Decorator pattern to wrap an existing function and changes its behaviour.
What it does is passing the function instance as a parameter into a decorate function.
def decorate_http_operation(func):
def wrapper(*args, **kwargs):
receive_request()
func(*args, **kwargs)
return_response()
return wrapper
Now any specific operation e.g. do_something() is passed in as parameter. Then the wrapper do the common behaviours plus calling do_something(). And finally return the modified version (i.e. the wrapper) of the function.
To use it:
new_func = decorate_http_operation(do_something)
new_func ()
So in this way, if we want to handle a homepage request:
new_func = decorate_http_operation(handle_homepage)
new_func ()
if we want to handle download:
new_func = decorate_http_operation(handle_download)
new_func ()
...etc.
Basically, the decorate function is for combining the common bahaviours and the specific function passed in as parameter. One line of calling the decorator function is much more concise than calling multiple functions for the common behaviours.
Note that the Wrapper function in the decorator accepts (*args, **kwargs) as parameters, which mean it accpets positional paramters (*args) and name-value pair parameters (**kwargs), so it is compatible with whatever parameters used by the do_something() function.
In Python there is a more handy way of doing the above decorating by using the @ tag
def decorate_http_operation(func):
...
@decorate_http_operation
def do_something():
... do stuff
do_something()
This is equivalent to passing do_something into decorate_http_operation. Now when you call do_something(), it is already including the common behaviours defined in the decorator. Even easier. Essentially, it is changing the behavours of the do_something() function, by appending the common behaviours. That is probably like decorating the do_something function.
Other scenarios using decorator are logging, adding prefix/subfix, print auxiliary information, etc.
In Python Flask, you have
@app.route('/homepage', methods=['GET'])
def get_home_page():
....
The app instance has already had the 'route' decorator function defined, so you just need to define the actual operations function and decorate it. Pretty handy! The compiler will probably append all the common GET behavivours for the '/homepage' url to your get_home_page implementation.