首页 > 技术文章 > 装饰器

xqr2018 2018-03-30 20:32 原文

装饰器:本质上是函数,用来装饰其他函数,为其他函数添加附加功能
原则:1.不能修改被装饰的函数的源代码
      2.不能修改被装饰的函数的调用方式
【即装饰器相对于被装饰的函数来说是透明的,它感觉不到装饰器的存在】
 
实现装饰器的知识储备:
1.函数即‘’变量‘’
2.高阶函数
3.嵌套函数
 
高阶函数+嵌套函数--->装饰器
 
函数即‘’变量‘’:定义一个函数,跟定义一个变量一样,就是把函数体存放到内存当中,然后用函数名指向内存地址,而当这个变量没了之后(用del回收),这个内存空间就会被回收(这个是python的内存回收机制);而匿名函数,跟有变量名的函数不同,因为没有函数名,所以需要赋予一个变量名以免被回收eg:a = lambda x:x*3
 
高阶函数:必须符合下面两个条件其中一个
1.把一个函数名当做实参传给另外一个函数(在不修改被装饰函数的情况下为其添加功能)
2.返回值中包含函数名(不修改函数的调用方式)
 
嵌套函数:在一个函数的函数体内用def申明一个函数
 
要调用函数grandpa,必须分别在函数里面调用子函数
 
高阶函数+嵌套函数--->装饰器的实现:
import time

def timer(func):
def deco():
start_time = time.time()
func()
end_time = time.time()
print('the running time is %s'%(end_time-start_time))
return deco

def test1():
time.sleep(3)
print('the func test1')

test1=timer(test1)
test1()

这段函数,实现了装饰器的作用,既没有改变源代码,也没有改变函数的调用方式,一般在函数定义前用@装饰器的名字代表test1 = timer(test1),即

import time

def timer(func):
def deco():
start_time = time.time()
func()
end_time = time.time()
print('the running time is %s'%(end_time-start_time))
return deco

@timer #等价于test1 = timer(test1)
def test1():
time.sleep(3)
print('the func test1')

test1()
当被装饰的函数带有参数时,应该想到用函数组,即可变长参数,所以,装饰器的正确打开方式应该是:
def timer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
print('the running time is %s'%(end_time-start_time))
return wrapper

@timer #等价于test1 = timer(test1)
def test2(name,age):
print(name,age)

test2(kevin,23)

调用func相当于调用wrapper函数,但是wrapper函数没有返回值,如果func这个函数是有返回值的,那岂不是函数的结果被改变了?要怎么处理呢?
这个时候,我们可以将func函数赋予给一个变量,再在最后return
eg:
def timer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
res=func(*args,**kwargs)
end_time = time.time()
print('the running time is %s'%(end_time-start_time))
return res
return wrapper

@timer
def test2(name,age):
print('name:',name,age)
return “from test2”

print(test2('kevin','23'))

关于装饰器加参数...:
即在装饰了一个功能的基础上在增加一个功能,如下:

import time
def login(auth_type):
    def timer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
res=func(*args,**kwargs)
end_time = time.time()
print('the running time is %s'%(end_time-start_time))
if auth_type == 'local':
print('-------local-------')
elif auth_type == 'ldap':
print('=======ldap=======')
return res

return wrapper
return timer

@login(auth_type='local')#由于return timer,相当于@timer,只不过为了给最外面的函数定义再增加一个参数
def test1():
time.sleep(3)
print('the func test1')
@login(auth_type='ldap')
def test2(name,age):
print(name,age)
return 'hello'

test1()
test2('kevin','23')
不能直接在原本的wrapper里面添加一个参数,因为@timer等价于test1=timer(test1),参数的个数是确定的了

推荐阅读