python 装饰器

本贴最后更新于 1021 天前,其中的信息可能已经时移世易
一、什么是装饰器
1. python装饰器是用于拓展原来函数功能的一种函数目的是在不改变原函数的情况下给函数增加功能
2. 装饰器是通过闭包实现所以讲装饰器首先得知道什么是闭包
二、什么是闭包
什么是闭包
1. 一个定义在函数内部的函数闭包可以让变量脱离了该函数的作用域也依然能被访问到
2. 定义一个嵌套函数内函数内部使用外函数变量让外函数将内函数当做参数返回这样就组成了一个闭包

举个例子
def outer(x):
    def inner():
        y = x * 2
        return y
    return inner
result=outer(10)
print(result())
执行过程分析
1. 调用外函数(outer)将参数赋值给x此时x=10
2. 执行外函数(outer)内函数(inner)不会被执行
3. 执行到return语句此时返回内函数inner对象(不会被调用)
4. 此时外函数(outer)执行结束的时候发现内部函数会用到自己的临时变量
5. 此时外函数(outer)临时变量就不会释放会绑定给内函数内函数(inner)继续使用
6. 此时result得到的结果就是inner引用(对象)调用result就等于执行内函数(inner)
三、函数装饰器
原理
1. 闭包的特性
2. 函数可以被当做参数传递

举个例子
import time
def outer(fn):
    def inner(x,y):
        start_time = time.time()
        result = fn(x,y)
        end_time = time.time()
        return result,end_time - start_time
    print('第一次',inner)
    return inner
  
@outer
def test01(x,y):
    time.sleep(5)
    return x+y
print('第二次',test01)
result=test01(1,2)
print(result)

执行过程分析
第一步执行到@outer相当于把函数 test01 赋值给 outer函数的fn参数
第二步执行outer函数返回inner函数
  1outer函数内部嵌套了一个inner函数利用闭包的原理inner函数可以直接使用outer函数fn的参数
  2在执行outer函数的时候,inner函数并未被执行是直接返回inner函数给test01函数
  3此时的test01函数已经被outer增加功能后返回的inner函数替换掉了(第一次第二次打印结果就是证据)
第三步执行test01(1,2)此时相当于执行inner(1,2)函数参数直接传递给inner函数的x,y

内存地址变化
1. outer内存地址:`<function outer at 0x102e051e0>`
2. test01内存地址:`<function test01 at 0x103739158>`
3. inner内存地址:`<function outer.<locals>.inner at 0x103739268>`
4. test01内存地址:`<function outer.<locals>.inner at 0x103739268>`
四、类装饰器
原理
1. 通过重写call方法实现

写法
import time
class Decrator:
    def __init__(self,fn):
        self.fn = fn

    def __call__(self, *args, **kwargs):
        start_time = time.time()
        result = self.fn(*args, **kwargs)
        end_time = time.time()
        west_time = end_time - start_time
        return result,west_time

@Decrator
def sum(x,y):
    print(sum)
    return x+y
sum(1,2)

执行过程分析
1. __call__()方法的作用其实是把一个类的实例化对象变成了可调用对象使得类实例对象可以像调用普通函数那样对象名()的形式使用
2. 当调用sum函数的时候会先将sum函数作为参数传给Decrator类(通过类的`__init__`初始化函数接收)此时 fn =sum(x,y)(只是实例未调用)
3. 此时sum函数传递进来的参数为元组就会赋值给了`__call__`方法args
4. 此时sum函数传递进来的参数为字典就赋值给了`__call__`方法kwargs
5. `__call__`方法中做一系列操作之后return self.fn(*args) 再传递给sum函数 *
6. *` __call__`方法返回的函数self.fn(*args是被装饰后的函数实例然后传给sum函数执行 
7. `__call__`必须要return装饰后的函数否则会报错
回帖
请输入回帖内容 ...