为什么说想到Python中的装饰器是天才
只需一个@符号就能分析、测试和重复使用你的代码
带着魔杖的仙女在Python代码中飞舞
软件中有没有什么是神奇的小魔法? 有,装饰器却非常接近!
如果说有一件事使Python难以置信地成功,那就是它的可读性。其他一切都取决于此:如果代码不可读,就很难维护。那么它也对初学者不友好--一个被不可读的代码弄得晕头转向的新手,有一天也不会尝试写自己的代码。
在装饰器出现之前,Python 已经是可读的,并且对初学者友好。但随着语言开始被用于越来越多的事情,Python 开发者感到需要越来越多的功能,同时又不至于杂乱无章,使代码无法阅读。
装饰器是一个完美实现功能的典型例子。它确实需要花点时间来理解,但这是值得的。当你开始使用它们时,你会注意到它们不会使事情过度复杂化,并使你的代码变得整洁而时髦。
在其他事情之前:高阶函数
简而言之,装饰器是处理高阶函数的一种整洁的方式。所以让我们先来看看这些
返回函数的函数 假设你有一个函数,greet() - 它向你传递的任何对象致意
假设你有另一个函数,simon() - 它在适当的地方插入 "Simon"
我们怎样才能把这两个函数结合起来呢? 在你看下面的内容之前先想一想。
def greet(name):
return f"Hello, {name}!"
def simon(func):
return func("Simon")
simon(greet)
The output is 'Hello, Simon!'.
输出是 "你好,西蒙!"
希望这对你了解函数返回一个函数有意义!
当然,我们可以直接调用greet("Simon")。然而,重点是我们可能想把 "Simon "放到许多不同的函数中。如果我们不使用 "Simon",而是使用更复杂的东西,我们可以通过把它打包到simon()这样的函数中来节省大量的代码行。
函数中的函数
我们也可以在其他函数中定义函数。这一点很重要,因为装饰器也会这样做。如果没有装饰器,它看起来像这样。
下面的函数是接受参数 gpa 成绩,如果是‘yes’或大于‘90’分则调用函数 congrats() 否则,调用函数 encourage()
#练习修改
def respect(gpa):
def congrats():
return "Congrats, Your gpa is excellent!"
def encourage():
return "You're have change to try again"
if gpa == "yes":
return congrats()
else:
return encourage()
gpa = '91' #'python'
gpa = "no"
print(f"{gpa},{respect(gpa)}")
#output:
no,You're have change to try again
另外找时间讲一个意外:
print(gpa > '90')
# 注意字符串的比较结果意外吧?
函数 respect() 返回一个函数; respect("yes") 返回祝贺函数, respect("brother") (或其他参数代替 "brother")返回鼓励函数。
要调用这些函数,请输入 respect("yes") 和 respect("brother"),就像普通函数一样。
明白了吗?那么你就可以为装饰者做好准备了!
代码是美丽的书呆子
在电脑屏幕前显示<code/is/beautiful>的女人
Python装饰器的ABC 带有@符号的函数 让我们尝试一下前面两个概念的组合:一个函数接收另一个函数,并定义一个函数。听起来很匪夷所思?考虑一下这个。
def startstop(func):
def wrapper():
print("开始...")
func()
print("完成了!")
返回包装器
def roll():
print("在地上打滚,笑死人了 XD")
roll()
#OUPUT:
Starting...
Rolling on the floor laughing XD
Finished!
最后一行确保我们不再需要调用
startstop(roll)()
roll()就足够了。你知道这个调用的输出是什么吗?如果你不确定的话,就自己试试吧!
现在,作为一个非常好的替代方案,我们可以在定义startstop()之后直接插入这个。
@startstop
def roll():
print("在地板上笑着打滚 XD")
这样做是一样的,但是在开始时就把roll()和startstop()粘在一起了。
增加了灵活性
为什么会有这样的作用?这不是要消耗和以前一模一样多的代码行吗?
在这种情况下,是的。但是一旦你要处理稍微复杂的东西,它就会变得非常有用。这一次,你可以把所有的装饰器,即上面的def startstop()
部分移到它自己的模块里。
也就是说,你把它们写进一个叫做decorators.py
的文件,然后在你的主文件中写上类似这样的内容。
from decorators import startstop
@startstop
def roll():
print("在地上打滚,笑死我了XD")
原则上,你可以不使用
decorators.py
来做这个。但这种方式使生活更简单,因为你不必再处理嵌套函数和无休止的括号计算了。
你也可以同时再嵌套其他装饰器。譬如计算函数执行时间的 exectime
from decorators import startstop, exectime
@exectime
@startstop
def roll():
print("在地上打滚,笑死人了 XD")
注意,我们还没有定义exectime()
,但你会在看到它。它是一个可以测量Python中一个过程所需时间的函数。这样的嵌套相当于这样的一行。
导入time
时间库
import time
# 用来计算持续时间的装饰器
# 被任何函数占用的时间。
def exectime(func):
# 在inner1内添加参数。
# 如果函数需要任何参数。
# 可以像这样添加。
def inner1(*args, **kwargs):
# 储存函数执行前的时间
begin = time.time()
func(*args, **kwargs)
# 储存函数执行后的时间
end = time.time()
print("Total time taken in : " , func.__name__, end - begin)
return inner1
现在就可以正常运行了
# 调用上面已经定义excetime函数!
from decoratorsRoll import startstop, exectime
@exectime
@startstop
def roll():
time.sleep(3)
print("Rolling on the floor laughing XD")
roll()
roll = exectime(startstop(roll))
print(roll())
Starting...
Rolling on the floor laughing XD
Finished!
Total time taken in : wrapper 3.0007543563842773
括号内的计数开始了! 想象一下,你有五六个这样的函数相互嵌套在一起。装饰器的符号不是比这种嵌套的混乱更容易阅读吗?
你甚至可以在接受参数的函数上使用装饰器。现在想象一下,在上面那行中有几个参数,你的混乱就会完成。装饰器让它变得整齐划一。
最后,你甚至可以向你的装饰器添加参数--比如@mydecorator(argument)。是的,你可以不用装饰器来做这一切。但是,我祝愿你在三周后重读你的无装饰器的代码时,会有很多乐趣......
女人站在桌子上,上面放着电脑、浓缩咖啡杯和花瓶
装饰者使一切变得更容易
应用:装饰者切入的地方 现在,我希望能说服你,装饰器使你的生活轻松三倍,让我们看看一些经典的例子,在这些例子中,装饰器基本上是不可缺少的。
测量执行时间 上面的写法稍作变化:time.perf_counter()
假设我们有一个叫做waste time()的函数,我们想知道它需要多长时间执行完函数。那么,就用一个装饰器:
import time
def measuretime(func):
def wrapper():
starttime = time.perf_counter()
func()
endtime = time.perf_counter()
print(f"Time needed: {endtime - starttime} seconds")
return wrapper
@measuretime
def wastetime():
sum([i**2 for i in range(1000000)])
wastetime()
十几行代码,我们就完成了! 另外,你可以在任意多的函数上使用 measuretime()
。
减缓代码速度
有时你不想立即执行代码,而是要等待一段时间。这时,减速装饰器就派上用场了。
import time
def sleep(func):
def wrapper():
time.sleep(300)
return func()
return wrapper
@sleep
def wakeup():
print("Get up! Your break is over.")
wakeup()
调用wakeup()
使得你可以休息5分钟,之后你的控制台会提醒你回去工作。
测试和调试
假设你有一大堆不同的函数,你在不同的阶段调用,而你对什么时候被调用失去了概览。通过对每个函数定义的简单装饰器,你可以使其更加清晰。就像这样。
def debug(func):
def wrapper():
print(f"Calling {func.__name__}")
return wrapper
@debug
def scare():
print("Boo!")
scare()
这里有一个更详细的例子。不过要注意的是,要理解这个例子,你必须检查如何用参数来装饰函数。不过,这还是值得一读的!
重用代码
这一点不言而喻。如果你已经定义了一个函数decorator(),你就可以在你的代码中到处撒上@decorator。说实话,我认为没有比这更简单的了。
处理登录
如果你有一些功能只有在用户登录后才能访问,使用装饰器也是相当容易的。我会让你参考完整的例子,但原理很简单:首先你定义一个类似login_required()
的函数。在任何需要登录的函数定义之前,你弹出@login_required
我想说,这让调用变得很简单!
语法糖--为什么Python是如此的甜蜜
Python有一个很大的诱惑力:它是如此容易理解,即使你不是一个受过训练的计算机科学家,而只是想让事情运转,帮你快速上手解决问题!
如果C++是一个橙子,那么Python就是一个菠萝:营养相似,但甜度要高三倍。装饰器只是其中的一个因素。
但我希望你已经明白了为什么它是如此大的一个甜味因素。合成糖为你的生活增添一些乐趣!这就是合成糖。没有健康风险,除了让你的眼睛粘在屏幕上。
我祝愿你有很多甜蜜的代码!
本文由 mdnice 多平台发布