What is this post about

Many people believe that decorator is one of the obscure concepts in Python.
Trust me, it is not. To be short, a decorator is a function that modifies other
functions via closures.

They are plenty detailed articles about what decorator it is so there is no need to write one more. If you are not familiar with it, you may want to check these:

In this article, I am going to use a simple but interesting example to show

  • how to colorize the output in command line
  • how to implement a switch case in Python (Python does not have built-in switch case)
  • a decorator with parameters

OK, let’s rock.

colorize your text

There is a set of escape sequences used to change the color of texts. So if we want to colorize a sentence, we just need to put the sentence between the color escape sequence and reset escape sequence. For example:

1
2
3
4
5
6
7
>> ORANGE = '\033[33m'
>> RED = '\033[31m'
>> GREEN = '\033[32m'
>> BLUE = '\033[34m'
>> RESET = '\033[0m'
>> print ORANGE + "Chinese New Year" + RESET
>> print GREEN + "Chinese" + GREEN + "New" + BLUE + "Year" + RESET

You will see

Chinese New Year

and

Chinese New Year

How to choose the color in code

As I said, Python does not support switch case. So we cannot switch the color name and choose the corresponding escaped sequence. Fortunately, dictionary would do the work.

1
2
3
4
5
6
7
8
9
10
11
def getColor(color):
# @param color: string like "red" or "yellow"
# @return the corresponding escape sequence.
# If not valid, return empty string
return {
RED = '\033[31m',
GREEN = '\033[32m',
BLUE = '\033[34m',
# and more ...
ORANGE = '\033[33m'
}.get(color, "")

The trick is, the dictionary’s built in get method. The first parameter here is key, the second optional parameter is default. As the docstring shows:

D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.

Use the decorator

Suppose we have a function to implement a task. It may has three kind of return string, if task is completed successfully, it returns “SUCCESS: blah blah …”, if the task is finished but we cannot ensure it’s corretness, it returns “WARNING: blah blah …”, if task failed, returns “ERROR: blah blah …”, how do we colorize these return strings?

Raw code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def colorize(*args):
def getColor(color):
return {
'black' : '\033[30m',
'red' : '\033[31m',
'green' : '\033[32m',
'orange' : '\033[33m',
'blue' : '\033[34m',
'purple' : '\033[35m',
'cyan' : '\033[36m',
'light_grey' : '\033[37m'
}.get(color, "")
def _colorize():
def wrapper():
RESET = '\033[0m'
text = func()
#if not isinstance(text, basestring):
# text = str(text)
level = text.split(':', 1)[0]
color = {
'SUCCESS': args[0],
'WARNING': args[1],
'ERROR': args[2]
}.get(level, '')
return "{0}{1}{2}".format(getColor(color), text, RESET)
return wrapper
return _colorize

@colorize("green", "orange", "red")
def do_task():
# working working ....
if (success):
return "SUCCESS: Yeah~~"
elif (warning):
return "WARNING: wait, what?"
else:
return "ERROR: something went wrong here."

As you can tell, to make the decorator with parameters, we need to put it in another decorator.

Decorator are often used as cache, profiler, logger, synchronization(acquire lock, drop lock) and so forth. One of my favourite library Click is also a wonderful example.

Happy Chinese New Year ~