python装饰器全解

python装饰器的作用 使得函数包装和方法包装变得更容易阅读和理解

1.一般语法和可能的实现

  • 作为一个函数
  • 作为一个类
  • 参数化装饰器
  • 保存内省的装饰器

2.用法和有用的例子

  • 参数检查
  • 缓存
  • 代理
  • 上下文提供者

Python 函数不同于其他编程语言,它可以作为第一类对象使用,这是关键。因为装饰器本质上还是函数

很多人对装饰器难以理解,原因是由于以下三点内容没有搞清楚:

关于函数“变量”(或“变量”函数)的理解

关于高阶函数的理解

关于嵌套函数的理解

那么如果能对以上的问题一一攻破,同时遵循装饰器的基本原则,相信会对装饰器有个很好的理解的。那么我们先来看以下装饰器的目的及其原则。

1、装饰器

装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

  • 不能修改被装饰的函数的源代码
  • 不能修改被装饰的函数的调用方式
  • 满足1、2的情况下给程序增添功能
    那么根据需求,同时满足了这三点原则,这才是我们的目的。因为,下面我们从解决这三点原则入手来理解装饰器。

等等,我要在需求之前先说装饰器的原则组成:

< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >

这个式子是贯穿装饰器的灵魂所在!

2、需求实现

improt time
def test():
    time.sleep(2)
    print("test is running!")
test()

给程序添加统计运行时间(2 second)功能

如何进行呢?

很显然,函数和变量是一样的,都是“一个名字对应内存地址中的一些内容”
那么根据这样的原则,我们就可以理解两个事情:

test1表示的是函数的内存地址

test1()就是调用对在test1这个地址的内容,即函数

如果这两个问题可以理解,那么我们就可以进入到下一个原因(关于高阶函数的理解)

那么对于高阶函数的形式可以有两种:

把一个函数名当作实参传给另外一个函数(“实参高阶函数”)

返回值中包含函数名(“返回值高阶函数”)

那么这里面所说的函数名,实际上就是函数的地址,也可以认为是函数的一个标签而已,并不是调用,是个名词。如果可以把函数名当做实参,那么也就是说可以把函数传递到另一个函数,然后在另一个函数里面做一些操作,根据这些分析来看,这岂不是满足了装饰器三原则中的第一条,即不修改源代码而增加功能。那我们看来一下具体的做法:

还是针对上面那段代码:

improt time

def test():
    time.sleep(2)
    print("test is running!")

def deco(func):  
    start = time.time()
    func() #2
    stop = time.time()
    print(stop-start)

deco(test) #1

装饰有参数的函数

improt time

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res 
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
    return "Returned value"
test()     

带参数的装饰器

import time

def timer(parameter):

    def outer_wrapper(func):

        def wrapper(*args, **kwargs):
            if parameter == 'task1':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task1 run time is :", stop - start)
            elif parameter == 'task2':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task2 run time is :", stop - start)

        return wrapper

    return outer_wrapper

@timer(parameter='task1')
def task1():
    time.sleep(2)
    print("in the task1")

@timer(parameter='task2')
def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()