1、装饰器基本介绍
(1)装饰器原理
- 装饰器在不改变原函数的调用方式的前提下,为原函数增加功能。
def w1(func):
def inner():
print("---正在验证权限----")
func()
return inner
def f1():
print("---f1---")
def f2():
print("---f2---")
# jy: 为增加原函数(f1)的功能, 此处改变了原函数的调用方式
innerFunc = w1(f1)
innerFunc()
"""
---正在验证权限----
---f1---
"""
# jy: 没有改变原函数的调用方式, 调用方式仍然为: f1()
f1 = w1(f1)
f1()
"""
---正在验证权限----
---f1---
"""
# jy: 不经过装饰, 原样输出: "---f2---"
f2()
(2)装饰器语法糖(@)
def w1(func):
def inner():
print("---正在验证权限----")
func()
return inner
#jy: 此处的 @w1 等价于: f1 = w1(f1)
@w1
def f1():
print("---f1---")
#jy: 此处的 @w1 等价于: f2 = w1(f2)
@w1
def f2():
print("---f2---")
f1()
"""
---正在验证权限----
---f1---
"""
f2()
"""
---正在验证权限----
---f2---
"""
2、多个装饰器装饰函数
# 装饰器-1
def makeBold(fn):
def wrapped():
print("----1---")
return "<b>" + fn() + "</b>"
return wrapped
# 装饰器-2
def makeItalic(fn):
def wrapped():
print("----2---")
return "<i>" + fn() + "</i>"
return wrapped
# jy: 两个装饰器装饰, 等价于如下代码逻辑(越接近被装饰函数的装
# 饰器会越优先对其进行打包装饰, 直到真正执行函数时, 最外层
# 的包的逻辑先执行, 随后再执行内层的):
"""
# 传入 makeItalic 的 test3 是最原始的 test3
test3 = makeItalic(test3)
# 传入 makeBold 的 test3 是以上经过装饰后的 test3
test3 = makeBold(test3)
"""
@makeBold
@makeItalic
def test3():
print("----3---")
return "hello world-3"
ret = test3()
print(ret)
"""
----1---
----2---
----3---
<b><i>hello world-3</i></b>
"""
3、装饰器中代码的执行逻辑
- 装饰器什么时候开始进行装饰(装饰器中的代码什么时候执行)?
def w1(func):
print("---正在装饰1----")
def inner():
print("---正在验证权限1----")
func()
return inner
def w2(func):
print("---正在装饰2----")
def inner():
print("---正在验证权限2----")
func()
return inner
# jy: 两个装饰器装饰, 等价于(越接近被装饰函数的装饰器会越优先
# 对其进行打包装饰, 真正执行被装饰函数时, 最外层的包的逻
# 辑先执行, 随后再执行内层的):
"""
f1 = w2(f1) # 传入 w2 的 f1 是最原始的 f1
f1 = w1(f1) # 传入 w1 的 f1 是以上经过装饰后的 f1
"""
@w1
@w2
def f1():
print("---f1---")
# f1 经过装饰后(还没执行 f1 函数), 就已输出以下内容:
"""
---正在装饰2----
---正在装饰1----
"""
# 调用 f1 后, 输出结果如下:
"""
---正在验证权限1----
---正在验证权限2----
"""
f1()
4、装饰无参函数
def func(functionName):
print("---func---1---")
def func_in():
print("---func_in---1---")
functionName()
print("---func_in---2---")
print("---func---2---")
return func_in
# jy: 等价于: test = func(test), 因此在还没执
# 行 test() 时, 就已经会输出如下:
"""
---func---1---
---func---2---
"""
@func
def test():
print("----test----")
# jy: 执行 test 时输出结果如下:
"""
---func_in---1---
----test----
---func_in---2---
"""
test()
5、装饰有参函数
(1)定长参数
- 当原被装饰函数有参数时,需要在装饰器里的闭包函数的定义中也定义相应数量的参数(为了确保被装饰器装饰后调用方式不变,使得调用返回的闭包时传入的参数被传入到原函数中)
- 因此,当装饰有参函数时,以下代码中的第 6、11、27 行中的函数参数个数必须保持一致。
def func(functionName):
print("---func---1---")
# jy: 如果缺失 a, b 参数, 会导致 test(11, 22) 的调用失败;
# 因为经过装饰器装饰后, 调用 test(11, 22) 相当于是调用
# 装饰器返回的闭包, 即此处的 func_in
def func_in(a, b):
print("---func_in---1---")
# jy: 如果缺失 a, b 参数传入, 会导致调用 test(11, 22)
# 函数失败; 因为 functionName 代表原函数, 其调用方
# 式必须与被装饰前的函数, 即 test(a, b) 保持统一
functionName(a, b)
print("---func_in---2---")
print("---func---2---")
# jy: 由于返回的函数句柄带两个参数, 故后续调用时也需要传入两个
# 参数; 且该函数句柄会将这两个参数直接传入原函数, 使得不改
# 变原函数的调用方式; 因此, 当原被装饰函数有参数时, 为了
# 确保被装饰器装饰后调用方式不变, 需要在装饰器里的闭包函数
# 的定义中也定义相应数量的参数, 然后传入原函数中
return func_in
# jy: 等价于: test = func(test), 此时被装饰后会输出如下:
"""
---func---1---
---func---2---
"""
@func
def test(a, b):
print("---- test- a=%d, b=%d ---"%(a, b))
# jy: 调用函数时, 输出如下:
"""
---func_in---1---
---- test- a=11, b=22 ---
---func_in---2---
"""
test(11, 22)
(2)不定长参数
def func(functionName):
print("---func---1---")
# jy: 采用不定长参数的方式满足所有函数的传参情况
def func_in(*args, **kwargs):
print("---func_in---1---")
# jy: 此处 args 前要带 *, kwargs 前要带 **; 如果不
# 带, 则 args 为元祖, kwargs 为字典, 与后续被装
# 饰函数的参数不一定一致
functionName(*args, **kwargs)
print("---func_in---2---")
print("---func---2---")
return func_in
# jy: 装饰 test 函数时, 输出如下:
"""
---func---1---
---func---2---
"""
@func
def test(a, b, c):
print("----test- a=%d, b=%d, c=%d ---"%(a, b, c))
# jy: 装饰 test2 时, 输出如下:
"""
---func---1---
---func---2---
"""
@func
def test2(a, b, c, d):
print("----test- a=%d, b=%d, c=%d, d=%d ---"%(a, b, c, d))
# jy: 调用 test 时, 输出如下:
"""
---func_in---1---
----test- a=11, b=22, c=33 ---
---func_in---2---
"""
test(11, 22, 33)
# jy: 调用 test2 时, 输出如下:
"""
---func_in---1---
----test- a=44, b=55, c=66, d=77 ---
---func_in---2---
"""
test2(44, 55, 66, 77)
6、装饰带返回值的函数
def func(functionName):
print("---func---1---")
def func_in():
print("---func_in---1---")
# jy: 保存返回值
ret = functionName()
print("---func_in---2---")
# jy: 把返回值返回到函数调用中
return ret
print("---func---2---")
return func_in
# jy: 装饰 test 函数时, 输出如下:
"""
---func---1---
---func---2---
"""
@func
def test():
print("----test----")
return "haha"
# jy: 执行被装饰函数时, 输出如下:
"""
---func_in---1---
----test----
---func_in---2---
"""
ret = test()
# jy: test return value is haha
print("test return value is %s" % ret)
7、通用装饰器(装饰各种函数)
def func(functionName):
def func_in(*args, **kwargs):
print("-----记录日志-----")
ret = functionName(*args, **kwargs)
return ret
return func_in
# 1) 装饰带返回值函数 ------------------------------------------------
@func
def test():
print("----test----")
return "haha"
# jy: 执行被装饰函数时, 输出如下:
"""
-----记录日志-----
----test----
"""
ret = test()
# jy: test return value is haha
print("test return value is %s" % ret)
# 2) 装饰无参函数 ---------------------------------------------------
@func
def test2():
print("----test2---")
# jy: 执行被装饰函数时, 输出如下:
"""
-----记录日志-----
----test2---
"""
a = test2()
# jy: test2 return value is None
print("test2 return value is %s" % a)
# 3) 装饰有参函数 --------------------------------------------------
@func
def test3(a):
print("-----test3-- a=%d --" % a)
# jy: 执行被装饰函数时, 输出如下:
"""
-----记录日志-----
-----test3-- a=11 --
"""
test3(11)
8、带参装饰器
(1)普通带参装饰器
def func_arg(arg):
def func(functionName):
def func_in():
print("---记录日志- arg=%s --" % arg)
if arg == "heihei":
functionName()
functionName()
else:
functionName()
return func_in
return func
# jy: 带参装饰器的执行流程如下:
# 1) 先执行 func_arg("heihei") 函数, 该函数的返回结果
# 是 func 函数句柄(即普通不带参装饰器)
# 2) @func, 即普通不带参装饰器逻辑, 区别仅在于装饰器中的闭
# 包的 arg 对应为 "heihei";
# 3) 使用 @func 对 test 进行装饰, 当执行 test() 时会执
# 行相应的逻辑
@func_arg("heihei")
def test():
print("--test--")
# jy: 同理如上; 可见, 带参装饰器能在运行时, 基于传入装饰器中的
# 参数的不同选择执行不同的功能
@func_arg("haha")
def test2():
print("--test2--")
# jy: 执行被装饰函数 test 时, 输出结果如下:
"""
---记录日志- arg=heihei --
--test--
--test--
"""
test()
# jy: 执行被装饰函数 test2 时, 输出结果如下:
"""
---记录日志- arg=haha --
--test2--
"""
test2()
(2)通用带参装饰器(装饰函数可带参数、可有返回值)
def func_arg(arg):
def func(functionName):
def func_in(*args, **kwargs):
print("---记录日志- arg=%s --" % arg)
if arg == "heihei":
print("==== 传入装饰器的参数为 heihei ====")
ret = functionName(*args, **kwargs)
else:
print("==== 传入装饰器的参数为其它 ====")
ret = functionName(*args, **kwargs)
return ret
return func_in
return func
@func_arg("heihei")
def test1():
print("--test1--")
# jy: 执行被装饰函数时, 输出结果如下:
"""
---记录日志- arg=heihei --
==== 传入装饰器的参数为 heihei ====
--test1--
"""
test1()
@func_arg("heihei")
def test2(a, b):
print("--test2--")
return a + b
# jy: 执行被装饰函数时, 输出结果如下:
"""
---记录日志- arg=heihei --
==== 传入装饰器的参数为 heihei ====
--test2--
"""
res = test2(2, 3)
# jy: 5
print(res)
@func_arg("haha")
def test3():
print("--test3--")
# jy: 执行被装饰函数时, 输出结果如下:
"""
---记录日志- arg=haha --
==== 传入装饰器的参数为其它 ====
--test3--
"""
test3()
9、使用装饰器的注意事项
- 装饰器的实现过程中,被装饰后的函数其实已经是另外一个函数(函数名等函数属性会发生改变)
functools
包中的wraps
装饰器可消除这样的副作用:实现装饰器时,最好在装饰器中的闭包定义之前加上@functools.wrap(func)
(即对装饰器中的闭包进行进一步装饰,传入的参数为函数句柄),使得可以保留原函数func
的函数属性(如函数名等)。
(1)不加@functools.wrap(func)
,被装饰函数的属性发生改变
def my_decorator(func):
def wrapper(*args, **kwargs):
'''decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
# jy: 输出: "wrapper decorator", 因为 example 经过装
# 饰后, 函数属性发生了改变;
print(example.__name__, example.__doc__)
(2)加@functools.wrap(func)
,被装饰函数保留原有属性
import functools
def my_decorator(func):
# jy: 对装饰器中的闭包进行进一步装饰(装饰时传入的参数为函
# 数句柄 func);
@functools.wraps(func)
def wrapper(*args, **kwargs):
'''decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
# jy: 输出: "example Docstring"
print(example.__name__, example.__doc__)
标签:基础,---,test,func,3.1,print,装饰,def
From: https://blog.csdn.net/m0_66491750/article/details/136862391