python装饰器
python装饰器
python中函数的参数传递
python中对象的查找顺序:局部命名空间===>全局命名空间===>python内置
一般函数的不同类型参数的排列顺序为:位置参数 ===> 默认参数 ===> *args ===> 关键字参数 ===> **kwargs
闭包函数
当一个函数需要参数而又不可直接从函数的参数列表直接传递时,可在函数体外部给定参数,然后将参数与函数体整体缩进到另一个外部函数的内部,最后外部函数return内部函数的内存地址。
注:也就是用外部函数用一个大麻袋包住参数和内部函数,内部函数的不可直接传递的参数通过外部函数从麻袋口投递。另外投递的参数不可处于全局命名空间。
def outter():
#投递参数
def inner(*args,**kwargs):
#投递进来的参数
*args,**kwargs
return inner #注意,这里是inner不是inner()
那么这就是闭包函数,闭包函数可实现参数的传递。
初识装饰器
现有一场景:需要为某个函数扩展功能,有两点要求,一不可修改源代码,二不可修改函数调用方式。
def index(x, y, z): time.sleep(1) print("x is %s,y is %s,z is %s" % (x, y, z)) return "xyz"
def home(name): time.sleep(1) print(“Hello,%s!” % name) return name
假设现在要添加一个计算index函数耗时的功能。
import time def timer(*args, **kwargs): start = time.time() index(*args, **kwargs) stop = time.time() print("time:",stop - start)直接在源代码基础上扩展功能,然后封装到函数里(这样修改了函数的调用方式)
原函数调用index(*args, **kwargs),现函数调用timer(*args, **kwargs)
在上面的基础上,为多个函数扩展功能,这里为index和home函数添加计算耗时功能。
import time def timmer(func): def timer(*args, **kwargs):#这里传递参数是给func()使用的 start = time.time() func(*args, **kwargs) #func本身需要参数传入,此处意为func是index还是home #这里不可通过timer参数列表传入,所以使用闭包函数外包 stop = time.time() print("time:",stop - start)return timer这里还是修改了调用方式,改进如下:
func = timmer(func) #func可以为其他任意函数
虽然此func非彼func,但是从使用者角度来看,调用方式是一样的
现在还有问题是这个func装的还不对
import time def timmer(func): def timer(*args, **kwargs): start = time.time() res = func(*args, **kwargs) #将实际的func返回值赋值给res stop = time.time() print("time:", stop - start) return res #返回resreturn timer装的不对的地方在于,装的func实际执行的是timer函数,timer函数没写return的情况下
return默认返回None,要将实际的func的返回值返回。
装饰器语法糖
装饰器的语法糖:在被装饰的函数的上方一行(只能是上方的第一行)加上@和装饰器名称。 注意:装饰函数需要写在被装饰函数的前面。
import time # 装饰函数需要写在被装饰函数的前面。 def timmer(func): def timer(*args, **kwargs): start = time.time() res = func(*args, **kwargs) stop = time.time() print("time:", stop - start) return resreturn timer装饰器的语法糖:在被装饰的函数的上方一行(只能是上方的第一行)加上@和装饰器名称
@timmer # index= timmer(index) def index(x, y, z): time.sleep(1) print(“x is %s,y is %s,z is %s” % (x, y, z)) return “xyz”
@timmer # home = timmer(home) def home(name): time.sleep(1) print(“Hello,%s!” % name) return name
将timer函数装的更像func函数。被装饰过的index函数本质上是调用timer函数,所以被装饰过的index函数的函数属性诸如index.__name__,index.__doc__全部都是timer函数的函数属性。python提供了一个@wraps装饰器可以将func函数属性包装给timer函数。
import time
from functools import wraps #导入wraps
def timmer(func):
@wraps(func) #装饰timer函数,注意这个装饰器是有参数装饰器,这里将func传入,将func函数属性包装给timer函数
def timer(*args, **kwargs):
"""timer doc"""
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print("time:", stop - start)
return res
return timer</code></pre>
那么这时候就实现了对原func函数的调包,偷偷的将原index函数通过装饰器装饰出一个新的index函数,调用方式和以前一样,没有修改源代码,完成了功能扩展。
有参数的装饰器
在前面的装饰器中,函数timer实现了对原函数func的功能扩展,但是在传递参数的过程中注意到,timer函数的参数为(*args,**kwargs),这样传递参数的目的是为了将参数原封不动的传递给func函数,这样就固定死了timer函数的参数,要对多个不同的func函数扩展timer功能时,func成了变量,但此时没办法通过timer函数传递func参数(func本身是一个变量,需要参数),于是利用闭包函数传递参数。
在前面扩展功能时计算index函数耗时,扩展的计时功能没有引入参数,当扩展的功能还需要参数的时候,参数该怎样传递进去。
def timmer(func): #(2)
def timer(*args,**kwargs): #(1)
#功能扩展...(需要参数传入,例如这里代码为print(a))
res = func(*args,**kwargs)
return res
return timer
(1)处(*args,**kwargs)参数是固定的,无法添加参数
(2)处(func)是可以添加参数,但是我们使用装饰器的语法糖,传递参数会出现错误
@timmer # index= timmer(index) 这里语法糖限制是将@timmer下面的index函数内存地址当做唯一的一个参数传入index=timmer(index内存地址)
def index(x, y, z):
# 在(2)处添加的参数因语法糖的限制不能顺利传递
解决方法可通过闭包的方式再"嵌套"一层函数
import time
from functools import wraps
def deco(a):
def timmer(func):
@wraps(func)
def timer(*args, **kwargs):
"""timer doc"""
print(a)
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print("time:", stop - start)
return res
return timer
return timmer
@deco(5) # @deco(a) ---> @timmer ---> index = timmer(index) ---> index = timer
def index(x, y, z):
"""index doc"""
time.sleep(1)
print(“x is %s,y is %s,z is %s” % (x, y, z))
return “xyz”
注意:deco函数外不需要再用闭包"嵌套"函数,因为这里的deco函数的参数已经没有了限制,内部需要什么函数就在deco()里传递什么参数。