首先函数是可以调用的对象:
import time
def new():
print(time.time())
A = new
A()
如果有一个需求,保证new()不变的情况下,增加new()函数的功能,添加一行输出等等。
在函数运行期间动态增加功能的方式叫做装饰器decorator
从定义上理解decorator的满足要素:
- 首先不能动原函数
- 其次不能改原函数的调用方式,原来运行new()调用,增加了装饰器之后执行new()依然可以调用装饰后的函数
现有一个new()函数:
import time
def new():
print(time.time())
new()
输出时间是格林尼治时间到现在的秒数,如果添加一个需求,当函数运行时输出时间,用我们熟悉的时间格式展示出来,
假定函数名称是 ti
直接将new函数当作参数传到ti里边是一种形式,但是这改变了函数new的调用方式,
如果使用new = ti(new),将ti(new)作为新的new再调用new,这样调用方式就没有改变,
但是调用new以后相当于new()--------> ti(new)() 这种形式,
这时候可以采用闭包的形式,在ti内部返回一个函数wrapper,执行wrapper可以返回new,
就变成new()-------------> ti(new)() ---------> wrapper()
开始实践:
import time,datetime
def ti(func):
def wrapper():
print(datetime.datetime.now())
return func()
return wrapper
def new():
print(time.time())
new = ti(new)
new()
new = ti(new)可以用更简洁的方式:
@ti
@ti放在new函数上一行,它作用于new()函数,等同于new = ti(new)
一个简单的装饰器就出来了。
如果new函数需要参数怎么办?
new传入参数,在调用方式不变的情况下,就是变更之后的new传入参数,相当于给ti(new)传入参数,相当于wrapper传入参数,
import time,datetime
def ti(func):
def wrapper(*args,**kwargs):
print('now: %s'%datetime.datetime.now())
return func(*args,**kwargs)
return wrapper
@ti
def new(sec=''):
print(time.time(),sec)
new('seconds')
解决传入参数。
此时执行
print(new.__name__)
输出结果:
wrapper
本来的new已经变成了wrapper,不行,原来的是new,不能变
functools模块可以做到(functools可以把原始的new的__name__属性复制到wrapper中,否则有些依赖函数签名的代码执行会出错。)
@functools.wraps(func)
他要修改wrapper,所以放在wrapper上边,等同于
wrapper = functools.wraps(func)(wrapper)
import functools,time,datetime
def ti(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print('now: %s'%datetime.datetime.now())
return func(*args,**kwargs)
return wrapper
@ti
def new(sec=''):
print(time.time(),sec)
new('seconds')
这时候的print(new.__name__)
输出就会显示new了,解决。
hold on
如果ti需要传入参数呢。。。
ti传入参数的位置只能在@ti处,那是在ti函数外唯一一次可以传入参数的机会,传入的形式只能是@ti('canshu')
先分析这个形式的意思,@ti后边跟的是 ('canshu') ,然后再是new()函数,也就是new = ti('canshu')(new)
如果在ti外部再加一层函数newti,它包含参数('canshu'),然后返回一个ti函数,ti执行的时候传入new,后边就不用变了
如下:
import functools,time,datetime
def newti(text):
def ti(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print('now: %s\n%s'%(datetime.datetime.now(),text))
return func(*args,**kwargs)
return wrapper
return ti
@newti('end')
def new(sec=''):
print(time.time(),sec)
new('seconds')
解决。
暂时理解到这里。