前言
最近在做AIops的项目,基本上有关代码的我都直接跳过,因为实在是看不懂,索性直接看思路、链路等。但是学着学着,学到langchain,根本看不懂了,再加上有时候会使用 Flask 框架去让AI 写一些代码,也是看不太懂,我就觉得我真的需要重新复习一下 py 了。今天复习完装饰器(这个是Python 中非常重要的一个内容了),我真的感觉大彻大悟。还记得第一遍学的时候根本看不懂,花了很长时间去研究它,最后还是懵懵懂懂,不思其所以然,今天我就来讲讲我对装饰器的一个理解,希望对大家有所帮助,同时有错误之处也请及时提出。
为什么需要装饰器?
首先想象一个场景:公司让你开发一个程序,你在古法编程,正开心的写代码呢,突然上级提出一个要求:每个函数都要写一段相同的逻辑,你傻傻的将每个函数都加上那一段逻辑,写完了,这个程序有一百多个函数都使用到了那段逻辑。过了两天,领导又说,那一段逻辑要修改一下,你非常之痛苦,只能一个一个函数改了。到下班点了,同事看你在忙什么,你将情况跟他说明了一下,同事说:你是傻子吗,直接用装饰器装饰一下不就好了吗。你恍然大悟…..
上述场景说明,没有装饰器的话,会造成大量重复代码,同时增加后续维护成本。因此装饰器就是可以在不修改原函数内部代码的前提下,通过包装函数来扩展或增强其功能。
它的核心思想就是:将业务逻辑与公共附加逻辑分离,提高代码的复用性、可维护性和扩展性。
如果我现在想测试某个函数的执行时间
import time
def func1():
print('in func1')
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1) # 将函数本身做为参数传递进去
func1()
如上:fun1 函数就是原函数,但是想要加一个记录函数执行时间的逻辑,这就用到装饰器了。
这里我们可以使用 Python 中的一种特殊的语法结构语法糖,来更为简便的使用装饰器。
我们将上述代码修改如下:
import time
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
# @timer 等价于定义函数后执行 func1 = timer(func1)
@timer
def func1():
time.sleep(1)
print('in func1')
func1()
装饰带参数的函数
装饰带参数的函数
import time
def timer(func):
def inner(a):
print("开始前")
func(a)
print("开始后")
return inner
@timer
def func1(a):
print(f'{a} world!!!')
func1("hello")
装饰带多个参数的函数
import time
def timer(func):
def inner(*args, **kwargs):
print("开始前")
func(*args, **kwargs)
print("开始后")
return inner
@timer
def func1(name,age):
print(f'{name}的年龄是{age}')
func1("lz","19")
带参数的装饰器
以上都很容易理解,带参数的装饰器就比较难理解了,现在我一步一步带你来理解它
看到hello = timer2("配置参数")(hello),就意味着给装饰器参数了,这种结构就表明要再加一个函数,也就是timer2,这就是第一版;但是为了美化,我们不妨将timer函数包进timer2中,也就是第二版;第三版就是将hello = timer2("配置参数")(hello)变成语法糖的形式@timer2("配置参数");第四版就是使用@wraps将inner函数装饰成hello函数(wraps 装饰器,用于帮助创建装饰器时保留被装饰函数的元数据(如名称、文档字符串等)。使用 @wraps 可以确保装饰后的函数看起来像原始函数,这样有助于调试和文档生成。)
# 第一版
def timer(fn):
def inner():
print("start...")
fn()
print("end...")
return inner
def timer2(a):
print(a)
return timer
def hello():
print("hello world!!!")
hello = timer2("配置参数")(hello)
hello()
# 第二版
def timer2(a):
print(a)
def timer(fn):
def inner():
print("start...")
fn()
print("end...")
return inner
return timer
def hello():
print("hello world!!!")
hello = timer2("配置参数")(hello)
hello()
# 第三版
def timer2(a):
print(a)
def timer(fn):
def inner():
print("start...")
fn()
print("end...")
return inner
return timer
@timer2("配置参数")
def hello():
print("hello world!!!")
hello()
# 第四版
from functools import wraps
def timer2(a):
def timer(fn):
@wraps(fn)
def inner():
print("start...")
fn()
print("end...")
return inner
return timer
@timer2("配置参数")
def hello():
print("hello world!!!")
hello()
print(hello.__name__) # 查看函数的名称
可以发现,这个参数是在被装饰函数函数调用前就已经存在了,也就是一装饰就存在了,我们就可以利用这个参数做很多事情,就比如通过装饰器收集函数信息,做存储后续使用执行
from functools import wraps
from urllib.request import urlopen
fnDict = {}
def timer2(a):
def timer(fn):
# 为什么收集写在这?——因为原函数被装饰的那一刻,就已经开始收集了,没有等被装饰函数执行
fnDict[a] = fn
@wraps(fn)
def inner(*argc, **kwargs):
fn(*argc, **kwargs)
return inner
return timer
@timer2('/ip')
def handle():
content = urlopen('https://myip.ipip.net').read().decode('utf-8')
print(content)
@timer2('/hello')
def hello():
print('hello world')
# 可以加入命令行
cmd = input('请输入命令:')
if cmd in fnDict:
fnDict[cmd]()
可以发现,只要是被装饰的函数,就会被收集起来(因为原函数被装饰的那一刻,外面两层函数已经执行,函数名已经被收集,不用等到被装饰函数调用),而且函数名会和装饰器参数绑定(字典)只要你想要的参数命中装饰器的其中一个参数,就会触发其下面的函数执行。
以上是不是很像Flask框架,其实本质都是一样的,命中装饰器参数后执行其下面的函数,只不过Flask实现稍微复杂一点。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
装饰器固定结构
from functools import wraps
def decorator_with_args(param):
def actual_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"装饰器参数: {param}")
return func(*args, **kwargs)
return wrapper
return actual_decorator
@decorator_with_args("配置参数")
def my_function():
pass
总结
今天我们从0开始了解装饰器形成过程,并了解到装饰器的作用,以及在Flask中的应用,希望此文章对你学习装饰器有一定帮助噢~







