前言:
是这样的,背景是我在写自定义log函数的时候,用到了装饰器,一时间我想不起来当初咋写的了。
复习一下。
个人能力有限,仅供参考。
1.什么是装饰器,我们为什么要用装饰器?
1. 装饰器的本质是一个Python函数
2. 装饰器可以在其他函数不需要任何代码变动的前提下,增加额外功能
3. 装饰器的返回值本质上也是一个函数对象
4.常用的装饰器场景有:插入日志,性能测试,权限 等等等
我最开始需求是插入日志(别问为什么不用logging)
到后面在自定义,性能测试等等也在用
2.从最简单的无参数做起
下面举一个最简单的例子
def outer(func):
def inner():
print(" ====before function running==== ")
func()
print(" ====after function running==== ")
return inner
@outer
def testfunc():
print("Here is main function work")
if __name__ == '__main__':
testfunc()
简单梳理一下流程。
在我执行一个函数的时候,我需要看到其他的额外信息,但是我不想修改/改变这个函数的完成性/封闭性
我写了一个装饰器,@
输出结果:
====before function running====
Here is main function work
====after function running====
问题1:return inner 和return inner()有什么区别,why?
TypeError: 'NoneType' object is not callable
报错, inner函数是个内嵌函数,存货周期仅限于outer函数之内,outer想把他返回回来,失败(是这样吗?)
尝试1:那我能不能让在outer函数返回一个字符串看看?
def outer(func):
def inner():
print(" ====before function running==== ")
func()
print(" ====after function running==== ")
return "wuhu"
@outer
def testfunc():
print("Here is main function work")
报错,
testfunc()
TypeError: 'str' object is not callable
从形式上来看,就是inner函数无返回,但是outer函数返回了一个字符串,但是outer函数参数是一个函数,返回的字符串没有调用(是这样吗?)
那么很快,我就开始进行魔改
def outer(func):
def inner():
print(" ====before function running==== ")
resdata = func()
print(" ====after function running==== ")
return resdata
return inner ##这里写 inner inner() 和 "inner" 三种情况都不一样
@outer
def use4test():
print("Here is main function work")
datadic = {
"info" : "detect info"
}
return datadic
if __name__ == '__main__':
res = use4test()
print(res)
这里的逻辑是,key函数( use4test() )中有运行,有数据返回,经过测试,是可以在加上附加信息的同时,把信息传递出来,达到了初步的标准。
====before function running====
Here is main function work
====after function running====
{'info': 'detect info'}
这是输出结果
3.函数带多个参数,装饰器应该怎么做?
在实际需求中,往往key函数都是有参数的,而且往往不止一个,甚至千奇百怪,
举个例子
from time import ctime, sleep
def ftfunc(func):
def timef(*s, **gs):
print("[%s] %s() called" % (ctime(), func.__name__))
return func(*s, **gs)
return timef
@ftfunc
def foo(*s, **gs):
print(f"===here s is {s} ===")
print(f"===here gs is {gs} ===")
if __name__ == "__main__":
foo()
foo(1)
foo(1,2)
foo(1,2,3)
stu = {
'name' : 'bob',
'age' : 20
}
foo(1,2,3,**stu)
这里的逻辑是,添加描述信息,可以看到函数被调用的时间。
返回结果如下:
[Wed Feb 8 16:19:40 2023] foo() called
===here s is () ===
===here gs is {} ===
[Wed Feb 8 16:19:40 2023] foo() called
===here s is (1,) ===
===here gs is {} ===
[Wed Feb 8 16:19:40 2023] foo() called
===here s is (1, 2) ===
===here gs is {} ===
[Wed Feb 8 16:19:40 2023] foo() called
===here s is (1, 2, 3) ===
===here gs is {} ===
[Wed Feb 8 16:19:40 2023] foo() called
===here s is (1, 2, 3) ===
===here gs is {'name': 'bob', 'age': 20} ===
这里我们能够看到,也就是装饰器内部的函数,入参只要和key函数保持一致就可以了。
4.那么问题来了,能不能多个装饰器叠加用?
学习技术要多问为什么,先质疑,再质疑,再躺平
开摆!!!
思考一下情形:我有三个装饰器,A输出描述,B输出时间。
我的三个函数f1,f2,f3.
f1我要看描述,
f2我要看时间
f3我全都要!!!!
###一个函数使用多个装饰器
from time import ctime
def ftfunc(func):
def timef(*s, **gs):
print("[%s] %s() called" % (ctime(), func.__name__))
return func(*s, **gs)
return timef
def outer(func):
def inner(*s, **gs):
print(" ====before function running==== ")
res = func(*s, **gs)
print(" ====after function running==== ")
return res
return inner
@outer
@ftfunc
def foo(*s, **gs):
print(f"===here s is {s} ===")
print(f"===here gs is {gs} ===")
if __name__ == "__main__":
foo()
foo(1)
foo(1,2)
foo(1,2,3)
stu = {
'name' : 'bob',
'age' : 20
}
foo(1,2,3,**stu)
看结果
====before function running====
[Wed Feb 8 16:30:28 2023] foo() called
===here s is () ===
===here gs is {} ===
====after function running====
====before function running====
[Wed Feb 8 16:30:28 2023] foo() called
===here s is (1,) ===
===here gs is {} ===
====after function running====
====before function running====
[Wed Feb 8 16:30:28 2023] foo() called
===here s is (1, 2) ===
===here gs is {} ===
====after function running====
====before function running====
[Wed Feb 8 16:30:28 2023] foo() called
===here s is (1, 2, 3) ===
===here gs is {} ===
====after function running====
====before function running====
[Wed Feb 8 16:30:28 2023] foo() called
===here s is (1, 2, 3) ===
===here gs is {'name': 'bob', 'age': 20} ===
====after function running====
这表明了,装饰器也是按顺序执行,
2023年2月8日-V001 未完待续,
看网上相关还很多,但是在这个项目中,我已经够用了