Python “decorator” is a language construct that lets you define a wrapper function g to another function f, such that, when f is called, your wrapper g is called instead. (it can also be applied to methods, and class.)
The syntax for “decorator” is
@name1 immediately before the line of
Here's a decorator:
@g def f(): print("xyz")
is equivalent to
def f(): print("xyz") f = g(f)
That is ALL there is of Python decorators.
fis called, the wrapper
gwill be called instead.
fas its argument.
gmust return a function. Because, remember, the value of
fis a function. So, when
fis called such as
f(3), Python evaluates
g(f)must be a function too. (technically, it just need to be anything callable.)
Here's a example of decorator:
# -*- coding: utf-8 -*- # python # example of a decorator def gg(xx): print "gg called" def s(y): return y-1 return s @gg # ← this is decorator def ff(x): print "ff called" return x+1 print ff(3) # output: # gg called # 2
Note: the wrapper function
gg receives the function
ff. The wrapper can do anything with it. Typically, it'll eventually call
ff, but it doesn't have to.
In the above example, the wrapper
Here's 4 things to remember when writing a decorator:
gmust take a function as argument. (because it'll receive
gmust return a function. (because that'll be applied to
hmust take the same number/type of arguments as
hshould return the same type of value that
freturns a string,
hprobably should too. If
hprobably should too.
Here's a example of decorator. It (in effect) modifies the function's argument.
# -*- coding: utf-8 -*- # python # example of a decorator def gg(func): """a decorator for ff. Make sure input to ff is always even. If not, add 1 to make it even""" def hh(y): if y % 2 == 0: # even return func(y) else: return func(y+1) return hh @gg def ff(x): return x print ff(3) # 4 print ff(4) # 4 print ff(5) # 6 print ff(6) # 6
Note: decorator function
gg does not know what the decorated function
ff's arguments. However, since
gg is a wrapper, it can create and return a new function
hh, and have
hh check arguments then call
ff(args). This works because whatever arguments passed to the original function
ff is passed to
hh can do arg checking, then call
When defining the decorator function, often you need to catch all possible arguments of the original function. Here's a example of how.
In the following example, the original function is called only if some global variable is true.
# -*- coding: utf-8 -*- # python # example of decorator that check condition to decide whether to call cc = True # ← the condition def gg(func): """a decorator for ff. If cc is true, run ff, else do nothing""" def hh(*args, **kwargs): # ← param must catch all args of ff """do nothing function""" pass if cc: return func else: return hh @gg def ff(*args, **kwargs): # ff can be sending email, or any callable with no return value print "ff" pass ff(3) ff(3,"Jane") ff(3,4, k="thank you") # if cc is true, then ff is called 3 times # (different set of arguments are used, to illustrate that our wrapper work well with them) # if cc is false, Nothing's done, ff is not called.
〔☛ Python: Function with Optional Parameter, Named Parameter, Infinite Parameters〕
Decorator itself can have parameters.
For example, the following decorator:
@g(3) def f(): print("xyz")
is equivalent to
def f(): print("xyz") f = g(3)(f)
There's nothing special about this. In the simplest decorator example,
@g followed by
def f: gets transformed into
f = g(f). Now, if we replace
g(x), we get
f = g(x)(f).
Decorators can get complex, because it's transforming code and doing function applications and involves nested functions. In this case, just remember that:
g(x)must return a function, let's call it
h1. This will receive
h1must take a function. (
h1must return a function (let's call it
Here's a example:
# -*- coding: utf-8 -*- # python # example of decorator with argument def gg(num): """a decorator for ff. Ignore ff. Simply return num.""" def h1(f1): def h2(f2): return num return h2 return h1 # gg(1) must return a function h1, such that h1 accept function (the ff), and also return a function (to be applied to ff's args) @gg(1) def ff(x): return repr(x) + " rabbits" print ff(3) # 1 print ff(4) # 1 print ff(5) # 1