c

codeye

V1

2022/10/27阅读:23主题:默认主题

为什么说想到Python中的装饰器是天才

为什么说想到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就是一个菠萝:营养相似,但甜度要高三倍。装饰器只是其中的一个因素。

但我希望你已经明白了为什么它是如此大的一个甜味因素。合成糖为你的生活增添一些乐趣!这就是合成糖。没有健康风险,除了让你的眼睛粘在屏幕上。

我祝愿你有很多甜蜜的代码!

分类:

后端

标签:

后端

作者介绍

c
codeye
V1