一、装饰器
装饰器本身是个闭包函数,即函数参数是个函数,在函数里面定义了一个函数,并且返回值是个内部函数。
装饰器的特点:
不修改已有函数的源代码;也不修改已有函数的调用方式;给已有函数添加额外的功能
1.1 装饰器的原理
def decorator(fn): # fn:目标函数.
def inner():
'''执行函数之前'''
fn() # 执行被装饰的函数
'''执行函数之后'''
return inner
示例:
def myDecorator(func):
def innerFunc(name,age):
print('装饰器只执行中')
func(name,age)
print('装饰器只执行结束')
return innerFunc
def myfunc(name,age):
print(f'my name is {name} ,{age} years old!')
mydec = myDecorator(myfunc)
mydec('xiaoming',22)
#装饰器只执行中
#my name is xiaoming ,22 years old!
#装饰器只执行结束
装饰器函数参数为 被装饰的函数,返回值是内部函数,因此执行这个装饰器函数 首先以被装饰函数为实参传给装饰器函数,然后返回值复制给一个变量,这个变量就是装饰器内部函数的别名,然后用被装饰函数的实参传入这个函数 进行调用
1.2 装饰器语言糖写法
只需要在被装饰的函数上面加上 @装饰器函数名。 因此如果希望多个函数被装饰,只需要再函数上面加上 @装饰器函数名
那么调用被删除函数时,就会自动调用装饰函数的内部函数.
def myDecorator(func):
def innerFunc(name,age):
print('装饰器只执行中')
func(name,age)
print('装饰器只执行结束')
return innerFunc
@myDecorator
def myfunc(name,age):
print(f'my name is {name} ,{age} years old!')
myfunc('xiaoming',22)
print(myfunc.__name__) # 输出innerFunc
发现一个有趣的现象,myfunc 的名字 却变成了innerFunc了. 需要把原始函数的__name__等属性复制到innerFunc()函数中,否则,有些依赖函数签名的代码执行就会出错。
使用functools.wraps 完成此功能
import functools
def myDecorator(func):
@functools.wraps(func)
def innerFunc(name,age):
print('装饰器只执行中')
func(name,age)
print('装饰器只执行结束')
return innerFunc
@myDecorator
def myfunc(name,age):
print(f'my name is {name} ,{age} years old!')
myfunc('xiaoming',22)
print(myfunc.__name__) #输出myfunc
1.3 不定长参数 装饰器的语法格式:
# 通用装饰器
def decorator(fn):
def inner(*args, **kwargs):
print('装饰器只执行中')
result = fn(*args, **kwargs)
print('装饰器只执行结束')
return result
return inner
def func(arg1, arg2,arg3)
return 'xxxxx'
#调用
func(参数1,参数2,arg3=参数3) #args 收集 参数1,参数2, kwargs 收集参数3
示例:
def myDecorator(func):
def innerFunc(*args,**kwargs):
print('装饰器只执行中')
func(*args,**kwargs)
print('装饰器只执行结束')
return innerFunc
@myDecorator
def myfunc(name,age,faverate):
print(f'my name is {name} ,{age} years old, my faverate is {faverate}')
myfunc('xiaoming',22,faverate='shuxu')
#装饰器只执行中
#my name is xiaoming ,22 years old, my faverate is shuxu
#装饰器只执行结束
1.4 带多个装饰器的使用
多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
def make_div(func):
def inner(*args, **kwargs):
return "<div>" + func() + "</div>"
return inner
def make_p(func):
def inner(*args, **kwargs):
return "<p>" + func() + "</p>"
return inner
# 装饰过程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
@make_div
@make_p
def content():
return "快乐家族"
result = content()
print(result)
因此先使用make_p对content函数装饰,然后使用make_div对content装饰
1.5 装饰器带参数
带参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,...)
实现方式: 在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。
# 相加
flaglist = []
def make_addOrmux(flag):
def decorator(fn):
flaglist.append(flag)
print('make_addOrmux--'+"decorator")
def inner(a, b):
if (flag == '+'):
print("做加法")
elif (flag == '*'):
print("做乘法")
return fn(a, b)
return inner
return decorator
@make_addOrmux("+")
def make_add(a, b):
return a + b
@make_addOrmux("*")
def make_mux(a, b):
return a * b
for flag in flaglist:
print(flag)
print(make_add(2, 3))
print(make_mux(2, 3))
#输出
make_addOrmux--decorator
make_addOrmux--decorator
+
*
做加法
5
做乘法
6
在装饰器的外层定义函数make_addOrmux() ,由这个函数接受参数装饰器的参数,并返回内部装饰器
可以看出 带参数的外部装饰器 内部函数decorator 先运行,打印"make_addOrmux--decorator", 并将参数flag假如flaglist中,
也就是说 装饰器带参数这种方式,即使没有运行函数,也会直接先运行内部函数decorator 最外部的外层装饰器, 被装饰的函数被调用时,内部装饰器函数inner才会执行,这个与不带参数的装饰器的区别
1.6 类装饰器的使用
类装饰器就是用一个类来装饰 一个函数,那么首先要让类编程一个可调用的对象callable, 就也是要实现类的__call__ 函数,函数在__call_里面调用
class decoratorClass(object):
def __init__(self, fn):
self.__fn = fn
def __call__(self, *args, **kwargs):
# 添加装饰功能
print("装饰功能开始")
self.__fn(*args, **kwargs)
print("装饰功能结束")
@decoratorClass
def comment(a,b):
print('a+b={}'.format(a+b))
comment(4,5)