装饰器本质上就是一个函数,主要是为其余的函数添加附加的功能,装饰器的原则有如下两个:python
装饰器能够简单的理解为:高阶函数+嵌套函数+闭包闭包
高阶函数我在前面的博客中已经讲过了,在这里我再简单的说一下吧。
高阶函数:若是一个函数接收的参数是一个函数名,或者返回值是函数名,只要知足任意一个条件,这个函数就称为高阶函数。app
def foo(): print("the result from foo") def bar(func): func() print("the result from bar") bar(foo)
在上面的例子中,我定义了两个函数,foo()和bar(),在调用bar()函数的时候,我将foo()做为一个参数传给了bar(),运行就会获得foo()的结果,这样的函数就能够成为高阶函数。函数
def foo(): print("the result from foo") def bar(func): return func bar(foo)
调用bar()函数 返回的就是foo()的内存地址,这也能够称为高阶函数。code
函数嵌套:函数嵌套我在前面也已经讲过了,其实就是函数的内部再声明函数,很好理解的一个概念,举个例子:以下内存
def foo(): print("the result from foo") def bar(): print("the result from bar") bar() foo()
在上面这个例子中,我在函数foo()的内部又写了一个bar()函数,并在下面调用了该函数,这种方式就叫作函数嵌套,能够嵌套不少层,只要注意函数缩进问题作用域
在Python中闭包的表现形式能够理解为:若是在一个内部函数里,对在外部做用域(但不是在全局做用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。
单从上面的定义可能很难理解,我下面用一个简单的程序说明一下:博客
def foo(): a=1 b=2 def bar(): c=3 return a+b+c return bar print(foo()())
在上面这个例子中,bar()就是foo()的一个内部函数,在bar()的局部做用于能够直接使用foo()的局部变量a,b,简单的说,这种内部函数能够访问外部函数变量的行为,就叫作闭包。class
为了能让你们更好的理解装饰器,我会分步作出这个实例。test
先定义一个函数name(),3秒后打印名字
import time def name(): time.sleep(2) print("my name is 尼古拉斯赵四") name()
如今有一个需求,我想要统计这个函数一个运行了多少秒,在不修改源代码的状况下,就须要给这个函数写一个装饰器来完成这个需求。
def timmer(func): #定义一个形参,就是为了接受name()这个函数,注意前面文章就已经强调过的:函数即变量---func=name def wapper(): #定义函数wapper(),用来接收name的参数 start_time=time.time() func() #实质上就是在运行test() stop_time=time.time() print("name()函数一共运行了%s 秒"%(stop_time-start_time)) return wapper import time @timmer #使用装饰器的方法,经过@+做为装饰器的那个函数名 def name(): time.sleep(2) print("my name is 尼古拉斯赵四") name() 运行结果: my name is 尼古拉斯赵四 name()函数一共运行了2.0002381801605225 秒
上面这个例子就是一个简单的装饰器,没有修改原函数的调用方法和返回值,装饰器timmer中用到了高阶函数+函数嵌套+闭包的知识,ok,完美。
def name(my_name,my_age): time.sleep(2) print("my name is %s,my age is %s"%(my_name,my_age)) name("尼古拉斯赵四",18)
我须要随机传入两个值,打印出他的姓名和年龄,若是原函数这样调用,使用上面的装饰器确定会出错,那就须要在装饰器函数中作以下修改:def wapper(my_name,my_age)
和func(my_name,my_age)
这两行加入相同的参数,也是能够的,但若是name函数我再修改呢,不传入两个参数了 ,传3个 或更多,每次都要去修改岂不是很麻烦,这就须要作一些改变了 。
def timmer(func): def wapper(*args,**kwargs): #这里用*args,**kwargs代替,这样 ,无论原函数传入多少个参数,均可以匹配 start_time=time.time() func(*args,**kwargs) #一样,接受任意多个参数 (若是不懂这个什么意思,翻看我前面函数篇的博客,有讲到) stop_time=time.time() print("name()函数一共运行了%s 秒"%(stop_time-start_time)) return wapper
def name(my_name,my_age): time.sleep(2) print("my name is %s,my age is %s"%(my_name,my_age)) return "尼古拉斯 你真年轻" print(name("尼古拉斯赵四",18)) 运行结果: my name is 尼古拉斯赵四,my age is 18 尼古拉斯 你真年轻
在这个函数中,我需求是在运行完函数返回"尼古拉斯 你真年轻"这句话,仍是用上面的装饰器返回值会是my name is 尼古拉斯赵四,my age is 18 name()函数一共运行了2.000795364379883 秒 None
,返回值是None而不是想要的结果,你们能够试一下,因此要加以下修改:
def timmer(func): def wapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) #将func()运行结果赋值给变量res stop_time=time.time() print("name()函数一共运行了%s 秒"%(stop_time-start_time)) return res #返回res,其实就是在返回name() return wapper
上面就详细写了装饰器的实现过程,就先写到这,后续我会丰富装饰器并作出一个通俗易懂的例子供你们参考,稍后一些时间也会发布在个人博客里,感兴趣的到时候能够看一下,但愿能够帮助你们对装饰器有更好的理解。