1.闭包
闭包就是变量在函数里定义后,函数运行后就不会存在了,但是这个变量在函数运行中被其中的嵌套函数引用了,就留在了嵌套函数里面,这就是闭。闭包是理解装饰器的基础。
#闭包
def main():
msg = 'i am writing log'
#嵌套函数
def mylog():
print(msg)
return mylog
colosure = main()
#这里没有输出
colosure()
#output:i am writing log
2.闭包转为装饰器
装饰器则是将一般的变量变为了函数,这个函数在后期会运行
def mylog(func):
print('logging')
def wrapper():
print('before')
func()
print('after')
return main
def main1():
print('i am main1')
mylog(main1)()
mylog(main1).__name__
'''
#print信息
logging
before
i am main1
after
logging
#output
'wrapper'
'''
3.简化
用@符号进行简化调用,简化后可以用于函数执行的性能测试
def mylog1(func):
print('logging')
def wrapper():
print('before')
func()
print('after')
return wrapper
@mylog1
def main1():
print('i am another')
main1()
'''
#print信息
logging
before
i am another
after
'''
4.对装饰器进行改进
我们可以看到我们虽然做到了在函数前后打印或做其他东西,但是函数的主体已经变了,不是mylog了,变成原来最初返回的函数体了。但是到这里我们可以做一下简化了
def mylog2(func):
def wrapper():
print('before')
func()
print('after')
return wrapper
@mylog2
def main2():
print('i am in main2')
main2()
print(main2.__name__)
'''
#print信息
before
i am in main2
after
wrapper
'''
简化后运行更简单了,不用两个括号了,这就是@的用处。
5.保留初始函数信息
函数体(名字等属性)依然是最后返回的wrapper,需要继续改进。引入functool中的wrapper函数,对属性等信息进行复制保留。它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。
from functools import wraps
def mylog3(func):
@wraps(func)
def wrapper():
print('before')
func()
print('after')
return wrapper
@mylog3
def main3():
print('i am in main3')
main3()
print(main3.__name__)
'''
#print信息
before
i am in main3
after
main3
'''
这里我们看到main3的函数不再被wrapper替换了
6.带参数的装饰器(3层)
有时候装饰器也需要带参数,这个时候为了要把参数传进去,需要在最外层套一层,用于传入装饰器的参数,这样就成了3层,再内一层才开始传入函数
#带参数的装饰器
def mylog5(logname = '1'):
def logdeco(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('before')
print(logname)
return func(*args, **kwargs)
return wrapper
return logdeco
@mylog5()
def main5():
print('i am in ',main5.__name__)
main5()
'''
before
1
i am in main5
'''
@mylog5(logname='2')
def main6():
print('i am in ',main6.__name__)
main6()
print(main6.__name__)
'''
before
2
i am in main6
'''
7.装饰器类(配合call函数使用)
甚至可以用建立一个装饰器类如下,让call函数(定义在类上的call函数)多接受一个函数作为参数,就可以再@logit的时候,直接用作装饰器了
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 现在,发送一个通知
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
# logit只打日志,不做别的
print('notify1')
pass
@logit()
def myfunc1():
pass
myfunc1()
'''
print信息
myfunc1 was called
notify1
'''
8.对类的继承和具化
作为类当然也可以继承,所以可以在这个基础上构造更具体的log函数,比如
class email_logit(logit):
'''
一个logit的实现版本,可以在函数调用时发送email给管理员
'''
def __init__(self, email='admin@myproject.com', *args, **kwargs):
self.email = email
super(email_logit, self).__init__(*args, **kwargs)
def notify(self):
# 发送一封email到self.email,省略
print('notify')
pass
@email_logit()
def myfunc2():
pass
myfunc2()
'''
print信息
myfunc2 was called
notify
'''
9.常用装饰器
@property,把类内的方法当作类属性使用,必须要保证类方法有返回值
@classmethod,类方法,不需要self参数,但第一个参数需要是表示自身类的cls参数
@staticmethod,静态方法,不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
#1.
class car():
def __init__(self,name,price):
self._name = name
self._price = price
@property
def car_name(self):
return self._name
@car_name.setter
def car_name(self,val):
self._name = val
def price(self):
return self._price
benz = car('benz',30)
print(benz.car_name)
#output:benz
benz.car_name='bmw'
print(benz.car_name)
#output:bmw
#2.
#3.
10.装饰器的顺序
最上面的是最外层
@a
@b
@c
def main():
pass
a(b(c(main())))
11.使用场景
打印日志,检查授权(如果有授权就执行,没有就不执行),性能测试,事务处理,缓存
这里贴一下缓存的代码
#装饰器实现缓存
def cache(func):
cache = {}
@wraps(func)
def wrap(*args):
# print(args)
if args not in cache:
cache[args] = func(*args)
#print(cache)
return cache[args]
return wrap
class Solution:
@cache
def fib(self, N):
if N < 2:
return N
else:
return self.fib(N - 2) + self.fib(N - 1)