《python核心编程》这本书一看就知道是搞技术的人写的,和我以前看qt的某些书籍一样。东一榔头西一棒子。不过还好,python的大部分我都知道了,看这本书不过是在查漏补缺,而且本身也有C/C++的基础。如果是刚入门的同学,如果看这本书能看懂,那真是天才了。
不过如果你同我一样,抱着查漏补缺的态度,那么这本书会有很多闪光点。其中一个就是这个python装饰器。
复合函数
学过数学的人,应该都知道复合函数或者算子吧:z=h。g。f (h,g是算子,f是g算子的定义域), 圈是复合符号.这样就形成了一个复合函数链。前面算子的值域是后面算子的定义域(算子的复合顺序是从右到左)。
如果我们把python中的普通函数看作f,那么装饰器就是后面的g,h 算子 ,返回的z是与f具有接受相同参数能力的新函数f_new,那么我门在使用的时候直接输入f(),实际上就是使用的f_new()。
无参数装饰器
- 现在简单来看下f_new=g。f的形式,也就是不带参数的装饰器。显然g的定义域是python的普通函数f,返回值是f_new。f_new与f具有接受相同参数的能力。
下面看看代码说明:
def foo():
print 'in foo()'
def wrap(func):
print 'in wrap'
return func
这个foo()函数是普通的python函数,它可以作为算子g的输入;下面的wrap定义域是func函数,返回的也是一个与输入func同类型的函数,显然它就是算子g
@wrap
def foo():
print 'in foo()'
foo()
现在有 foo=f_new=wrap 。 foo ; 现在再调用foo(),实际上就是调用的f_new()也就是 (wrap。foo) ()
带参数装饰器
- 现在来看代参数的装饰器,其数学表达式为:f_new=g(x)。f ;同样,f是我们的普通的python函数。不同的是先用算子g,生成一个新的算子g(x),g(x)的定义域才是f,返回的函数为与f同类型的f_new。
下面是代码说明:
def wrap_out(arg):
print 'in wrap_out,and arg is %s'%arg
def wrap_inner(func):
print 'in wrap_inner'
return func
return wrap_inner
@wrap_out('test1')
def foo()
print 'in foo'
foo()
现在就有foo=f_new=wrap_out(arg)。foo
写成方程组:
wrap_inner=wrap_out(arg)
foo=wrap_inner(foo)
有了上面的理解后,就应该非常清楚,包装函数内部应该是什么结构,应该返回什么。只要把函数的复合链写出来,就很容易搞清楚装饰器的结构。
比如上面的带参数的装饰器,我看过很多同学写的解释,反正就是很复杂。但是如果把foo=wrap_out(arg)。foo 写出来分析下。wrap_out带参数arg,而且它返回的值必须能接受foo函数,也就是wrap_inner=wrap_out(arg),wrap_inner必须能接受foo作为输入。再着,wrap_inner(foo)返回的值必须是一个和原foo接受统一的参数的函数,。那整个结构就很清楚了。
很多装饰器的复合
现在来看一个比较复杂的,双重的装饰器。
def wrap_out2(arg):
print('in wrap_out 2 ,and arg is %s'%arg)
def wrap_inner2(func):
print('in wrap_inner 2')
func('in wrap 2 call func')
def fun_new2():
print('in func_new 22')
return fun_new2
return wrap_inner2
def wrap_out(arg):
print('in wrap_out,and arg is %s'%arg)
def wrap_inner(func):
print('in wrap_inner')
func()
def func_new(h):
print(h)
return func_new
return wrap_inner
#foo_new=wrap_out2('world') o wrap_out('hello') o foo
@wrap_out2('world')
@wrap_out('hello')
def foo():
print('in foo')
foo()
输出结果为:
in wrap_out 2 ,and arg is world
in wrap_out,and arg is hello
in wrap_inner
in foo
in wrap_inner 2
in wrap 2 call func
in func_new 22
数学式子为:
foo=wrap_out2('world')。wrap_out('hello')。foo
通过输出结果可以判断其化解顺序为:
foo=wrap_inner2。wrap_out('hello')。foo
foo=wrap_inner2。wrap_inner。foo
也就是说算子化简是从左到右,而普通计算是从右到左。要是这样都不能理解装饰器,那就只有去撞墙了。^_^
还可以看到wrap_out 和wrap_out2的输入与输出是不同的,在数学上输入与输出相同称为变换,如果不同那么只能称为映射了。映射不能随意的匹配,在这里只能写成wrap_out2。wrap_out的形式,而不能颠倒它们的复合顺序。当然一般的装饰器,都是经过一定处理后,返回原函数,也就是变换。
综上:func=h([arg])。g([arg])。func =T 。func ;T就是一个算子,它的定义域和值域都是接受相同参数的 func 。老func 输入 T 返回同类型的 新func 。也就是说装饰器就是算子T,整个算子T是func空间的变换;over