时隔一年再看一遍装饰器,我悟了!!!
本文最后更新于7 天前,其中的信息可能已经过时,如有错误请发送邮件到2156936367@qq.com

前言

最近在做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中的应用,希望此文章对你学习装饰器有一定帮助噢~

觉得有帮助可以投喂下博主哦~感谢!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
Hello! I'm 臭企鹅!