python:简单的装饰器 ^-^

隐身守侯 提交于 2020-03-03 02:48:15

今天我们讲一讲python中的装饰器。可能初次接触装饰器的同学会觉得它很难,其实也就那么一回事儿,今天就让我们会会它!

装饰器

首先它的本质是函数,它的功能是为其他函数添加附加功能
ps:它有两个原则:
1.不能修改被修饰的函数的代码。
2.不能更改被修饰函数的调用函数。

我所认为的装饰器其实就是:
装饰器 = 高阶函数+函数嵌套+闭包
我们先说什么是高阶函数?
当函数(符合其中任意一个):

  1. 函数接收的参数是一个函数名。
  2. 函数的返回值是一个函数名。
    就是把高阶函数。
    那么我们考虑当只有高阶函数时,我们能否写出一个装饰器?
    现在我们只用高阶函数实现:运行foo函数时,添加功能:计算其运行时间并打印。试试看吧!
# 装饰器 = 高阶函数 + 函数嵌套 + 闭包
# 先玩玩高阶函数
import time


def foo():
    time.sleep(3)
    print('来自foo')


def timer(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print('函数的运行时间是%s' % (stop_time-start_time))

timer(foo)  # 改变了函数的调用方式
import time


def foo():
    time.sleep(3)
    print('来自foo')


def timer(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print('函数的运行时间是%s' % (stop_time-start_time))
    return func


foo = timer(foo)  # 没改变调用方式也没改变函数源代码但多运行了一次,不合格
foo()
# 总结高阶函数独自完不成装饰器

分析上述两段代码,我们会发现:第一种方式改变了函数的调用方式,第二种则多运行了一句代码。所以我们总结:高阶函数独自完不成装饰器!

所以我们需要函数嵌套,和闭包的概念。

函数嵌套:在函数中再定义函数。
闭包:优先使用自己的作用域。
这两个概念都是比较简单的,在这里不作过多解释。下面粘一段代码,供大家参考。

#  函数的嵌套与闭包

def father(name):
    print('我是%s' % name)

    def son():
        print('我的父亲是%s' % name)

        def ganud():
            print('我的爷爷是%s' % name)
        ganud()
    son()

# 闭包先使用自己的作用域。
father('liuruochen')
"""
最后结果是:
我是liuruochen
我的父亲是liuruochen
我的爷爷是liuruochen
"""

到这里大家对高阶函数,函数嵌套,闭包应该已经明了。
而装饰器=高阶函数+函数嵌套+闭包。
我们通过一段代码来分析。代码依旧是指行一个函数,通过装饰器给其附加一个计算运行时间的功能。

# 给test函数添加计算运行时间的附加功能
import time


def timer(func):     # 传进去test

    def warrp(*args, **kwargs):  # args = (name, age)
        # print(args)
        start_time = time.time()
        res = func(*args, **kwargs)       # 运行test , *args = name, age
        stop_time = time.time()
        print('函数的运行时间是%s' % (stop_time-start_time))
        return res
    return warrp


@ timer     # @ timer 就相当于进行了 test = timer(test)
def test(age, name):
    time.sleep(3)
    print('【%s】年龄大的【%s】函数运行完毕!' % (age, name))
    return '这是fan'

# test = timer(test)  # 这里拿到的是warrp的地址,当没有@timer时必须多谢这句代码。
res = test(18, 'liu') # 实际上运行的是warrp
print(res)
"""
结果:
【18】年龄大的【liu】函数运行完毕!
函数的运行时间是3.0004591941833496
这是fan
"""

大家可以看到通过@timer装饰器,我们使用test函数就多出了一种附加功能!
注:
给大家分析一下它的过程。
首先,导入time模块,定义test函数,timer函数(我说过装饰器本质就是一个函数)
我们主要分析这个timer函数,大家可以看到,它的传参是一个函数名,返回值也是一个函数名。那么:timer(test)得到的是warrp函数的地址。我们进行test = timer(test)之后,这时的test其实变成了warrp函数。然后我们再进行test(18,“liu”)实际上就是进行warrp(18,liu)
(这里注意warrp参数的问题 args **kwargs 这里不做解释,如果需要文末会有一些博客解释它们。)。之后在函数warr()中执行了test()函数。
https://www.jianshu.com/p/821492b3d3c6 :解释
arg **kwarg问题。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!