装饰器的本质:一个闭包函数
装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展
开放封闭原则
程序实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
而装饰器就很好的适应这个原则
装饰器运用到的知识
函数中嵌套函数;函数返回一个函数;将函数作为参数传给另⼀个函数。
def sayhi():
print('hi')
def saybefore(func):
print('who is he?')
return func
#saybefore就是一个装饰器
装饰器的实现
def decorator(F):
# Process function F
return F
@decorator
def func(): ... # func = decorator(func)
所以上面等同于
def saybefore(func):
print('who is he?')
return func
@saybefore
def sayhi():
print('hi')
# 运行发现saybefore被调用了!并返回了一个函数。
现在想要每次“说的内容不一样”:sayhi中可以打印其他内容
def saybefore(func):
print('who is he?')
return func
@saybefore
def sayhi(arg):
print(c)
sayhi('Hellow')
# -----------------------------
def saybefore(func):
def inner(arg):
print('who is he')
return func(arg)
return inner
装饰器的使用
日志
在系统中被调用(执行)的函数(程序),有很多种。
要求:打印被调用的函数名,并返回函数的结果,
试想一下不使用装饰器如何编写代码:每个函数除实现原有功能中都要添加返回自身函数名的代码
而有时又不希望打印函数名。。。
#使用装饰器,只要事先写好好需要扩展的功能,对需要扩展功能的函数修饰就可以了
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
'''Do some math'''
return x + x
result = addition_func(4)
装饰器的wraps作用
被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools模块中提供了一个叫wraps的decorator来消除这样的副作用。
#将上面代码中的@wraps(func)去掉,执行下面代码
print(addition_func.__doc__) #None
print(addition_func.__name__) #with_logging
#添加 @wraps(func)后,就变得“正常”了,wraps装饰装饰器内的函数,就会代指元信息和函数。
带参数的装饰器
@wraps(func)就是一个带参数的装饰器;上面使⽤@decorator语法时,是在应⽤⼀个以单个函数作为参数的⼀个包裹函数。我们可以编写⼀下能返回⼀个包裹函数的函数。
#将每个函数的运行,单独生成一份日志
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写⼊内容
with open(logfile, 'a') as opened_file:
# 现在将⽇志打到指定的logfile
opened_file.write(log_string + '
')
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
# Output: myfunc1 was called
# 现在⼀个叫做 out.log 的⽂件出现了,⾥⾯的内容就是上⾯的字符串
@logit(logfile='func2.log')
def myfunc2():
pass
myfunc2():
pass
myfunc2()
myfunc2()
# Output: myfunc2 was called
# 现在⼀个叫做 func2.log 的⽂件出现了,⾥⾯的内容就是上⾯的字符串