python装饰器

装饰器是python一个重要的部分,由它的名称我们就可以大致了解到它的功能:拓展其他函数。装饰器可以让我们的代码更加简洁,也更加pythonic。

首先,我们先回顾一下基础概念。

、在python中,如果调用一个函数不带括号时,调用的是这个函数本身,无需等待该函数执行完毕;如果调用一个函数带括号时,调用的是这个函数return的结果,需要等待函数执行完毕的结果。实例展示如下:

def demo1():
    print("hello 2022")
demo1()
print(demo1)
#结果依次如下:
#hello 2022
#<function demo1 at 0x015E77C0>

二、函数闭包

闭包就是引用自由变量的函数,这个函数保存了执行的上下文,可以脱离原本的作用于存在。

def dec():
    para = 'closure'

    # 嵌套一层 形成闭包
    def wrapper():
        print(para)
    return wrapper

# 获取一个闭包
closure =  dec()

# 执行
closure()

para是一个局部变量,在dec中执行后即被回收。在嵌套函数中使用了这个变量,也就是将局部para变量封闭在嵌套函数中,形成闭包。

三、python装饰器

介绍了以上基础概念之后,我们再来介绍python装饰器的详细用法。

现在有如下代码:

def func():
    print("hello")
    sleep(5)
    print(2022)

现在需求来了,我们想拓展一下原来的函数,在原有的基础下增加一个计算函数执行时间的模块。

我们首先想到的便是编写一个计时函数,可以直接完成我们的需求。代码如下:

from time import time, sleep
def timer():
    start = time()
    func()
    end = time()
    res = end - start
    print("执行时间为{:.3f}".format(res))
def func():
    print("hello")
    sleep(5)
    print(2022)
if __name__  == "__main__":
    timer()

#执行结果如下
#hello
#2022
#执行时间为5.000

现在我们已经完成了对func()函数执行时间的计算,值得庆幸的是我们现在只有一个函数需要计算它的运行时间,如果一个代码中有成千上万计像func()函数都需要计算他们的运行时间该怎么办?总不能为每一个函数添加一个计时器或者不断地修改timer函数,这样不仅对操作员还是机器都是一个巨大的折磨。这时我们便选用@装饰器来解决我们这些问题。

代码展示如下:

from time import time, sleep
def timer(c):
    print("this is timer")
    def wrapper():
        start = time()
        c()
        end = time()
        res = end - start
        print("执行时间为{:.3f}".format(res))
    return wrapper
@timer
def func():
    print("hello")
    sleep(5)
    print(2022)
if __name__  == "__main__":
    func()
"""
执行结果如下
this is timer
hello
2022
执行时间为5.000
"""

通过对比,我们发现timer函数中增加了wrapper函数,timer函数的返回值是wrapper函数,可以粗暴的理解为timer()=wrapper()。

现在来分析一下以上代码的工作流程:@timer及其一下四行为装载装饰器的过程,装饰器timer的参数是一个函数,返回值也是一个函数,作为参数的函数c()就在闭包函数内执行;在func()前使用@timer,就相当于为func()注入了新的功能,我们既不需要入侵原函数,也不用重复执行原函数。

接下来我们继续分析func()有参数的情况。

代码如下:

from time import time, sleep
def timer(c):
    print("this is timer")
    def wrapper(a, b):
        start = time()
        c(a, b)
        end = time()
        res = end - start
        print("执行时间为{:.3f}".format(res))
    return wrapper
@timer
def func(a, b):
    sleep(5)
    print("a+b=%d" % (a + b))
if __name__ == "__main__":
    func(4, 5)

"""
this is timer
a+b=9
执行时间为5.001
"""

需要注意的是,func()与wrapper()中的参数必须一致。

接下来我们要思考的问题便是:如果func()有无穷多个参数怎么办呢?有没有一个简单的方法可以传递参数?我们想到的是利用位置参数*args和关键字参数**kwargs。(*args与**kwargs的用法不在赘述)具体是怎么实现的呢?我们来看代码。

from time import time, sleep
def timer(c):
    print("this is timer")
    def wrapper(*args, **kwargs):
        start = time()
        c(*args, **kwargs)
        end = time()
        res = end - start
        print("执行时间为{:.3f}".format(res))
    return wrapper
@timer
def func(a, b):
    sleep(2)
    print("a+b=%d" % (a + b))
if __name__ == "__main__":
    func(4, 5)
"""
this is timer
a+b=9
执行时间为2.000

"""

显而易见,我们大大简化了代码。

接下来有两个例子供大家更好的掌握装饰器的用法。

import time
def timer(func):
    print("this is timer")
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        cha_zhi = end - start
        print("运行时间为%d" % cha_zhi)
    return wrapper
def login(func):
    print("this is login")
    def wrapper1(*args, **kwargs):
        user = input("please input your name:")
        password = input("please input your password:")
        if user == 'root' and password == 'root':
            print("login successful!")
            res = func(*args, **kwargs)
            return res
        else:
            print("error!")
    return wrapper1
@login
@timer
def index():
    time.sleep(0.5)
    print("from index")
index()
"""
this is timer
this is login
please input your name:root
please input your password:root
login successful!
from index
运行时间为0
"""
def dec1(func):
    print("1")
    def one():
        print("2")
        func()
        print("3")
    return one
def dec2(func):
    print("a")
    def two():
        print("b")
        func()
        print("c")
    return two
@dec1
@dec2
def test():
    print("test")
test()
"""
a
1
2
b
test
c
3
进程已结束,退出代码为 0

"""

 来看这块代码:当遇到两个装饰器时,执行的顺序是有下往上,即test=dec1(dec2(test)),先执行dec2(test),输出a,此时dec2()中的参数func指向test,返回two(),然后dec1(two), 输出1,dec1()中func指向two,返回one()。test()是实际被装载的函数,此时实际执行的是one(), 运行到func()时再执行two()。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值