Decorator is actually a function, or more strictly is a callable. It acts like the "decorator" design pattern in a sense.
2) How to define a decorator and how to call the decorator
@decorator(decorator_opt_args)
def func2be_decorated(func_opt_args)
...
For eg:
@dec
def func(arg1, ...):
...
The above "decorator" equals something like this: func = dec(func). So when we invoke func('abc'), what is actually doing is "dec(func)('abc')".
To be more elaborate:
def a_decorator(func): #<<<<< Take the original "func" as the parameter.
def new_func(*args, **argkw):
print 'in new_func'
return func(*args, **argkw)
print 'in a_decorator'
return new_func # <<< Return a new wrapper func, which usually takes the same args as the original one
@a_decorator
def f(args): # At this very point, a_decorator is executed ('in a decorator' will be printed),
print 'in f' # and "f" is rebound to "new_func" returned by a_decorator
print args
print type(f), f.__name__ # prints "<type 'function'> new_func"
How it works ?
a) @a_decorator ... is actually an immediate function call with "f" as the parameter.
"a_decorator" is executed immediately when the interpreter sees this definition (@a_decorator def f...).
b) After calling a_decorator, the "new_func" is returned, which will rebind this returned new function to "f" just like f = new_func.
c) If we have followed calls to "f", we are actually calling "new_func".
Note, the "a_decorator(f)" in the following explanation has already been executed when the interpreter sees the decorator definition.
f('abc') => a_decorator(f)('abc') => new_func('abc') # a_decorator(f) returns "new_func" => output: 'in new func' and then 'in f' and then 'abc'
What if decorator itself takes arguments ?
def a_decorator2(dec_args): #<<<<< decorator itself takes args.
def _a_nested_one(func): # <<<<< this nested one takes the original func as the parameter
print 'in _a_nested_one'
def new_func(*args, **argkw):
print 'in new_func'
return func(*args, **argkw)
return new_func # <<<<< this nested one returns new wrapper func which usually
print dec_args # takes the same args as the original one
return _a_nested_one # <<< Return a nested decorator, which usually takes original func as the parameter
@a_decorator2('hello') # <<<<< We need provide parameters here, what if we miss the parameter ?
def ff(args): # At this very point, a_decorator2 and _a_nested_one have been executed,
print 'in ff' # and "ff" is rebound to "new_func". So, it prints "hello" and "in _a_nested_one".
print args
First when Python interpreter encounters "@a_decorator2('hello')", it invokes a_decorator2('hello') immediately which prints "hello" and returns a nested decorator "a_nested_one" and the nested decorator is also called immediately, so "in _a_nested_one" is printed.
3) object as a decorator
The object must be callable which means it must defines the __call__() method
class EntryExit(object):
def __init__(self, f):
print "init EntryExit"
self._f = f
def __call__(self, *args, **argkw):
print 'Entering %s' %self._f.__name__
self._f(*args, **argkw)
print 'Exited %s' %self._f.__name__
@EntryExit
def func1(s): # At this point, EntryExit.__init__ has been called and func1 is rebound to an EntryExit instance
print 'inside func1() - %s' %s
print type(func1) # <class '__main__.EntryExit'>
func1('hello')
Equivalent function decorator version
def entry_exit(f):
def new_f(*args, **argkw):
print 'Entering %s' %f.__name__
f(*args, **argkw)
print 'Exited %s' %f.__name__
new_f.__name__ = f.__name__
return new_f