引子
更多理解的好例子,请看知乎的讲解。
再来看以下这个例子,如果你对下面这段Python
代码还有疑问,请复习Python
基础知识。
def a():
return 'This is function a!';
def b():
print 'This is function b!';
return a(); #返回另一个函数
def c(fun):
print 'This is function c!';
return fun(); # 返回参数本身,参数是一个函数
print a();
print # print起分隔作用
print b();
print
print c(a);
print
print c(b);
输出结果:
This is function a!
This is function b!
This is function a!
This is function c!
This is function a!
This is function c!
This is function b!
This is function a!
通过上面的这个例子,你要记住的就是,输入函数的参数可以是一个函数,返回值也可以是一个函数!
一开始理解起来确实比较困难,我们再深入一步
从一个实例出发:
假设你是一个重度强迫症患者,对于你写的程序里面的每一个函数,你在运行程序的时候,你一定要知道什么时候,什么函数被调用了。于是你写了如下的程序(节选自知乎:李冬)。
在这个程序中,有个公共函数log(),它的作用是输入一个函数作为参数,然后对输入的函数进行加工,使其多一个print 的功能,最后将加了功能的函数返回。有了这么一个功能后,你就能知道这个函数什么时候执行了!
def log(func):
def wrapper(*arg, **kw):
print 'Start %s' % func.__name__;
return func(*arg, **kw);
return wrapper;
@log
def func_a(arg):
pass
@log
def func_b(arg):
pass
@log
def func_c(arg):
pass
func_a('foo');
func_c('bar');
我们来看运行结果:
Start func_a
Start func_c
这样你能做到你的要求了,上述代码是个缩略版本,我们将其补全:
def log(func):
def wrapper(*arg, **kw):
print 'Start %s' % func.__name__;
return func(*arg, **kw);
return wrapper;
def func_a(arg):
pass
func_a = log(func_a);
def func_b(arg):
pass
func_b = log(func_b);
def func_c(arg):
pass
func_c = log(func_c);
if __name__ == '__main__':
func_a('foo');
func_c('bar');
这两者是等价的。上文中的@log
可以写在被装饰函数的前面,而第二个例子繁琐的写法中func_a = log( func_a)
这个语句要写在被装饰函数的后面。
当然你可以写的更繁琐一点,引入一个中间变量。
def log(func):
def wrapper(*arg, **kw):
print 'Start %s' % func.__name__;
return func(*arg, **kw);
return wrapper;
def func_a(arg):
pass
tmp = log(func_a);
func_a = tmp;
...
总结升华:
这个@log
就是一个典型的语法糖的作用。它叫做装饰器,意思就是:用一个公共函数(如这里的log())
来装饰函数(这里的func_a(),func_b()等)
! 使得被装饰函数有新的功能。
利用@这种语法糖作用你可以,少敲几次键盘。