在咱们的软件产品升级时,经常须要给各个函数新增功能,而在咱们的软件产品中,相同的函数可能会被调用上百次,这种状况是很常见的,若是咱们一个个的修改,那咱们的码农岂不要挂掉了(有人就说了 ,你笨呀,修改函数定义不就好了!同窗,你醒醒吧,若是要新加的功能会修改参数,或者返回值呢?)。这个时候,就是咱们装饰器大显神通的时候了。装饰器就能够实现,在不改变原函数的调用形式下(即函数的透明化处理),给函数新增功能的做用。如何实现,以及实现原理,下文会详解。python
装饰器,顾名思义就是起装饰的做用,既然是装饰,那么被装饰的对象是啥样就是啥样,不能有丝毫改变。在这里,咱们写装饰器就是必须把握不能修改被修饰函数的源代码这条铁律。如何遵循这条铁律,咱们还需还需作一些铺垫,必须先要了解三个概念,以下:函数
在python中,函数名其实就像是c语言的函数指针,表明的是咱们的函数地址,只有解释器获取到这个地址,它才会去执行这块内存的代码。所以,本质上,函数名就和不一样变量没什么区别,只不过函数名和普通变量所指代的那块内存的使用方式不一样罢了,这些都是底层解释器的机制所决定的,对于程序猿来讲,都是透明的,因此,咱们能够认为二者是没有区别的。spa
什么是高阶函数其实很简单,把握两个原则就好:翻译
只要知足这两个原则之一,就能够称之为是高阶函数。翻回头来看,这里出现了咱们上面说的函数名,仔细体会一下,咱们在这里不就是把其当成实参看待的吗?设计
什么是嵌套函数其实也很是简单,把握一个原则就好:指针
在这里须要强调的是,函数定义时是不会执行函数体的,就和定义变量是不会去读取变量里的内容同样。这一点相当重要,对于咱们理解装饰器实现原理很是有帮助。对象
有了上文的铺垫,在如今来详解一下如何写装饰器,就好理解多了。blog
其实装饰器本质上就是一个函数,它也具备函数名,参数和返回值。但在python中,咱们用“@auth”来表示。内存
@auth # 其等价于:func = auth(func) def func(): print("func called")
这个示例就是python中如何修饰func函数的格式,固然咱们尚未实现咱们的装饰器函数。咱们要注意的是注释里写的内容,咱们能够看出:产品
装饰器即然是个函数,又有上述介绍的等价关系,那咱们就能够这样设计咱们的装饰器:
前面作了大量的铺垫,就是想在这里揭示装饰器的实现机制,其实没什么什么的,很简单:
装饰器函数定义跟普通函数定义没什么区别,关键是函数体怎么写的问题。这里,为了便于理解,先用无参数的装饰器函数说明。
#装饰器函数定义格式 def deco(func): '''函数体...''' return func
#使用语法糖@来装饰函数,至关于“myfunc = deco(myfunc)” def deco(func): print("before myfunc() called.") func() print("after myfunc() called.") return func @deco def myfunc(): print("myfunc() called.") myfunc() myfunc() #output: before myfunc() called. myfunc() called. after myfunc() called. myfunc() called. myfunc() called.
由输出结果能够看出,咱们的装饰器并无生效。别跟我说装饰器只生效了一次,那是你们忽略了“@deco”的等效机制。解释到“@deco”时,会解释成“myfunc = deco(myfunc)”。注意了,前面我提到了,这里其实在调用deco函数的,所以,deco的函数体会被执行。因此output的前三行并非调用myfunc函数时产生的效果,那有怎能说装饰器生效了一次呢?第二步就是解决装饰器没生效的问题的。
#基本格式 def deco(func): def _deco() #新增功能 #... #... func() #别修饰函数调用 return_deco
下面给出个示例:
#使用内嵌包装函数来确保每次新函数都被调用, #内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象 def deco(func): def _deco(): print("before myfunc() called.") func() print("after myfunc() called.") # 不须要返回func,实际上应返回原函数的返回值 return _deco @deco def myfunc(): print("myfunc() called.") return 'ok' myfunc() #output: before myfunc() called. myfunc() called. after myfunc() called.
当完成了第二步时,其实装饰器已经完成了主要部分,下面就是对被修饰函数的参数和返回值的处理。这样才能真正实现装饰器的铁律。话很少说,直接上代码:
#基本格式 def deco(func): def _deco(*args, **kwargs) #参数透明化 #新增功能 #... #... res = func(*args, **kwargs) #别修饰函数调用 return res #返回值透明化 return_deco
经过上面的分析知:
透明化处理就是这么简单!至此,咱们的装饰器编写完成。给个示例吧:
#对带参数的函数进行装饰, #内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象 def deco(func): def _deco(*agrs, **kwagrs): print("before myfunc() called.") ret = func(*agrs, **kwagrs) print(" after myfunc() called. result: %s" % ret) return ret return _deco @deco def myfunc(a, b): print(" myfunc(%s,%s) called." % (a, b)) return a + b print("sum=",myfunc(1, 2)) print("sum=",myfunc(3, 4)) #output: before myfunc() called. myfunc(1,2) called. after myfunc() called. result: 3 sum= 3 before myfunc() called. myfunc(3,4) called. after myfunc() called. result: 7 sum= 7
装饰器即然也是函数,那么咱们也能够给其传递参数。我这里说的是:“@auth(auth_type = 'type1')”这中形式哟。先上个代码吧:
#基本格式 def deco(deco_type) def _deco(func): def __deco(*args, **kwargs) #参数透明化 #新增功能 #... #... print("deco_type:",deco_type) #使用装饰器参数 res = func(*args, **kwargs) #别修饰函数调用 return res #返回值透明化 return __deco return _deco
至此,就达到了经过装饰器来传参的目的。给个示例吧:
#让装饰器带参数, #和上一示例相比在外层多了一层包装。 #装饰函数名实际上应更有意义些 def deco(deco_type): def _deco(func): def __deco(*args, **kwagrs): print("before %s called [%s]." % (func.__name__, deco_type)) func(*args, **kwagrs) print(" after %s called [%s]." % (func.__name__, deco_type)) return __deco return _deco @deco("mymodule") def myfunc(): print(" myfunc() called.") @deco("module2") def myfunc2(): print(" myfunc2() called.") myfunc() myfunc2() #output: before myfunc called [mymodule]. myfunc() called. after myfunc called [mymodule]. before myfunc2 called [module2]. myfunc2() called. after myfunc2 called [module2].
若是说,我上面说的内容都理解了,那么这个东东,就太简单不过了。不就是把咱们的是装饰器当中被修饰的函数,对它进行装饰吗?但我在这里还想说的是,咱们换个角度看问题。咱们的关注点放在原来的被修饰的函数上,就会发现,NB呀,我能够给它添加若干个功能撒。给个示例吧:
def deco(deco_type): def _deco(func): def __deco(*args, **kwagrs): print("before %s called [%s]." % (func.__name__, deco_type)) func(*args, **kwagrs) print(" after %s called [%s]." % (func.__name__, deco_type)) return __deco return _deco @deco("module1") @deco("mymodule") def myfunc(): print(" myfunc() called.") @deco("module2") def myfunc2(): print(" myfunc2() called.") myfunc() #output: before __deco called [module1]. before myfunc called [mymodule]. myfunc() called. after myfunc called [mymodule]. after __deco called [module1].
注意结果哟,@deco("module1"),来修饰的deco("mymdule")的,和咱们想的是同样的,完美!