闭包与装饰器

闭包与装饰器

  • 闭包
    传参方法
    修改函数变量方法
  • 装饰器
    无参装饰器
    带参装饰器
    多装饰器装饰一个函数
    类装饰器
闭包
简单的概括一下就是:函数内部嵌套函数,且最里面的函数用到了外层函数的变量,那么这样的一个结构整体称之为闭包。
闭包一句话来概括就是:为我们提供了一种新的、为函数体传参的方法。
传参方法

假设我们现在 要实现一个y = ax+b的线性拟合功能,我们可以采取的传参方法有:

# 1 直接定义变量
# 优点:简单直观
# 缺点:定义变量多,繁琐。每次x变得,都需要进行改动
a,b= 1,2
x= 1
y = x+2

# 2 函数体封装
# 优点:对代码实现了一定程度得到封装
# 缺点:全局变量多,工程大时很容易产生不安全现象(当然还有其他的限制之处)
a,b = 1,2
def add(x):
    print(a*x+b)
add(1)

# 3 匿名函数
# 优点:书写简单,减少代码量
# 缺点:同上
a,b = 1,2
add = lambda x:a*x + b
add(1)

# 4 类的方法
# 优点:使用类扩展性强
# 缺点:代码量大,且本身类会自动继承新式类,占用内存大,添加大量可能无必要功能
class wrapper():
    def __init__(self,a,b):
        self.a = a
        self.b = b
    def __call__(self,x):
        print(self.a*x+b)
add = wrapper(1,2)
add(1)

# 5 闭包
# 优点:封闭性强,后续作为装饰器基础,能够在不改动已有函数调用方式的前提下为函数体添加新的功能
# 缺点:外层函数结束调用后,有些变量不会为立即释放(内部函数用到),因而会占用内存
def wrapper(a,b):
    def add(x):
        print(a*x+b)
    return add
add = wrapper(1,2)
add(1)
修改函数变量方法
# 1:全局变量
num = 100
def wrapper(num):
    def inner():
        #global num
        num = 200
        print(num)
    return num
add = wrapper(num)
add()
print(num)
# 此时相当于在函数内部从新定义了一个局部变量num,并使num指向200,想对全局变量num改变,采用global关键字即可

# 2:函数内部变量
def wrapper():
    num = 100
    def inner():
        nonlocal num
        print(num) # 1号输出
        num+=200
        print(num) # 2号输出
    return inner
add = wrapper()
add()
# 当遇到1号输出时,在inner函数内部无num变量,因此就会爆出num在调用之前未定义的错误。当需要修改内部变量时,需要采用nonlocal关键字,在最内层函数找不到情况下,会向外层函数查找
# !!!注意这里引用外部变量与修改外部函数变量是不一样的!!!

装饰器
装饰器:基于闭包的思想,在不改变原始函数调用方式的前提下,为原本的代码扩展新的功能
无参装饰器
import time

# 需求 计算每个函数运行的时间
def cal_time(f):
    def inner():
        start_time = time.time()
        res = f()
        end_time = time.time()
        print("运行共耗费%3.2fs" %(end_time-start_time))
        return res
    return inner

@cal_time   # 相当于 func1 = cal_time(func1),此时func1就指向inner的地址(类似于指针的作用)
def func1():
    print("...一号函数")
    time.sleep(2)

def func2():
    pass

def func3():
    pass

func1()
有参装饰器
import time

# 需求 计算每个函数运行的时间
def cal_time(f):
    def inner(*args,**kwargs): # 2
        start_time = time.time()
        res = f(*args,**kwargs) # 3
        print(args,kwargs)  #!!!!please attention!!!!
        end_time = time.time()
        print("运行共耗费%3.2fs" %(end_time-start_time))
    return inner

@cal_time
def func1(*args,**kwargs): # 4
    print("...一号函数")
    time.sleep(2)

def func2():
    pass

def func3():
    pass

func1(1,2,3,4,a=1) #1
# 运行流程:
# 1:@cal_time  <===> func1 = cal_time(func1),此时func1指向inner函数的内存地址(fun1,inner这些虽然充当函数名,其实都是变量)
# 2:func1(1,2,3,4,a=1)中的实参向2中的形参进行值传递,*args与**kwargs相当于分别对元组与字典进行解包操作。此时的args=(1,2,3,4),kwargs={'a':1}
# 3:3中根据4中所需参数形式进行值的传递。也就是说3中传参形式要与4中保持一致
带参装饰器(三层装饰器)
从上述我们可以发现,不带参的装饰器满足了我们为已有函数添加功能的需求,很大程度上使程序得到了扩展。但是有一点当我们需要对新加入的功能进行一定程度的区分与 限制时,就无法满足我们的需求了。
例如:我们要添加一个认证的功能,但是针对不同的操作,如登录、查询、付款、转账他们其实应当分配不同等级的认证。在涉及到重要信息、资产操作的情况下应该得到不同级别的处理。
再如:我们可以在web服务端获取客户端请求不同的页面时,根据其页面信息的不同,通过装饰器动态的返回相应的页面
通过带参的装饰器,可以基本满足日常功能的需求,使我们在不改变原始函数调用方式的前提下,为不同的函数传参以及动态的赋予不同“程度”的功能
import time

# 需求 为每个函数添加认证功能
def author(level):
    def cal_time(f):
        def inner(*args,**kwargs):
            if level == "ordinary":
                print("普通验证级别")
                res = f(*args,**kwargs)
            elif level == "pivotal":
                print("关键验证级别")
            else:pass
        return inner
    return cal_time

@author("ordinary") 
#其实就是先执行author(参数)返回cal_time,再转变为@cal_time
def func1(*args,**kwargs):
    print("...一号函数")
    time.sleep(2)

def func2():
    pass

def func3():
    pass

func1(1,2,3,4,a=1)
多个装饰器装饰一个函数
结论:哪个装饰器在前面,就先执行哪个装饰器!!!!!!
具体实现原理(感兴趣可了解,以下图为例)
1:Python解释器在加载语句时,一般是从上倒下依次执行。在遇见多个装饰器时会直接跳到函数顶部第一个装饰器处。@cal_time  <===> func1 = inner = cal_time(func1),此时func1变量指向inner
2:然后再向上加载装饰器,author("ordinary") <===> wrapper = author(inner) ===>@wrapper此时已将参数“ordinary”传递到函数体内部,此时func1指向inner
3:@wrapper <===> func1 = inner1 = wrapper(inner),此时func1指向inner1
4:func1()此时就相当于执行inner1()
结论:多个装饰器装饰一个函数时,最前面的装饰器最晚加载,但是最先执行(有点栈的味道)
import time

# 需求 为每个函数添加认证功能
def author(level):
    def wrapper(f):
        def inner1(*args,**kwargs):
            if level == "ordinary":
                print("普通验证级别")
                res = f(*args,**kwargs)
            elif level == "pivotal":
                print("关键验证级别")
            else:pass
        return inner1
    return wrapper

# 需求 为每个函数添加一个计算运行耗时功能
def cal_time(f):
    def inner(*args,**kwargs):
        start_time = time.time()
        res = f(*args,**kwargs)
        end_time = time.time()
        print("运行耗时为:%3.2fs" %(end_time-start_time))
        return res
    return inner

@author("ordinary")
@cal_time
def func1(*args,**kwargs):
    print("...一号函数")
    time.sleep(2)

def func2():
    pass

def func3():
    pass

func1(1,2,3,4,a=1)
类装饰器(不常见,但扩张性强)
import time

class Decoration():
    def __init__(self,f):
        self.f = f

    def cal_time(self,*args,**kwargs):
        start_time = time.time()
        self.f(*args,**kwargs)
        end_time = time.time()
        print("运行耗时为:%3.2fs" %(end_time-start_time))
    def author(self,*args,**kwargs):
        print("验证功能")
        return self.f()

    def __call__(self,*args,**kwargs):
        if args[0] =="verify":
            self.author(*args,**kwargs)
        else:
            self.cal_time(*args,**kwargs)


@Decoration    # 相当于实例化一个类
def func1(*args,**kwargs):
    print("...一号函数")
    time.sleep(2)

def func2():
    pass

def func3():
    pass

func1("verify",1,2,3,4,a=1)
# 根据第一个参数使用不同的装饰功能,规定接口如何调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值