Python中的闭包和修饰器

Python中的闭包

  1. Python对闭包的官方说法:闭包表示一个内部函数里对外部作用域(但不是全局作用域)的变量进行引用,就认为内部函数是闭包。
  2. 闭包可以形象的理解为一个封闭的包裹,这个包裹就是一个函数,且这个函数有一个特点,它将外部作用域中的变量也包裹进去了。
  3. 闭包意味着,如果调用一个函数A,这个函数A返回一个函数B,那么称这个返回的函数B为闭包。

1. 下面是对闭包的演示代码

def func(name): #func为外部函数A
    def func1(age): #func1为内部函数B,且内部函数将外部作用域中的变量name也包裹进去,形成闭包
        print('name:',name,'age:',age) #此处调用的name为外部作用域(func函数的作用域)中的变量
    return func1 #返回一个func1的函数对象,也就是一个闭包,其中带有变量name

my_age = func('BigBoss') #用变量my_age来接收func返回的函数对象
my_age(19) #相当于调用func1(19)
my_age = func('BigBigBoss')
my_age(20)
  1. 在上述代码中,当调用函数func(‘BigBoss’)时就产生一个闭包func1(),并用my_age来接收,注意此时func函数的传参name就是调用时func函数的实参。
  2. 在该示例中,闭包func1()包裹了外部变量name,这表示当函数func()的生命周期结束后,变量name依然会存在,应为它被闭包引用了,所以不会被系统回收,故才可以被闭包函数func1()和函数my_age()使用。
  3. my_age接受这个闭包后就相当于给闭包取了个叫做my_age的名字,故my_age(age) == func1(age),注意此时func1函数的传参age就是my_age函数的实参。

运算结果

name: BigBoss age: 19
name: BigBigBoss age: 20

2. 下面演示闭包的实例——使用闭包记录函数的调用次数

def hello_counter(num = 0): #默认参数num的初始化为0
    count = num #count作为相对于函数counter()的外部作用域中的参数
    def counter(): #闭包
        nonlocal count #告诉解释器这个count变量是在外部定义的
        count += 1 
        print('次数:',count)
    return counter #返回闭包

hello = hello_counter() #使用变量hello接收函数对象counter
hello()
hello() #调用两次函数hello()
  1. 在上述代码中,外部函数将num赋值给count,作为计数使用。
  2. 首先使用变量hello来接受函数hello_counter()返回的闭包counter(),故此时hello() == counter(),故每调用一次hello()就相当于调用一次counter(),而闭包counter()能够让外部变量count自增,因此可以达到计算函数调用次数的目的。
  3. 注:此处用到了python关键字nonlocal,该关键字的作用是告诉解释器这个count变量实在函数counter()的外部定义了,有了这个关键字,python就会从外层函数寻找变量count。

运算结果

次数: 1
次数: 2

3. 下面演示闭包的应用——装饰器

  1. 在python语言中,可以使用装饰器给不同的函数或类插入相同的功能,且不影响原有函数和类的功能,还能添加新的功能。
  2. python中使用@符号来实现装饰器,在定义装饰器装饰函数或类时,使用“@装饰器名称”的形式将符号“@”放在函数或类的定义行之前。
  3. 要想使用装饰器来装饰一个函数或类,必须先定义这个装饰器。在python中,定义装饰器的格式与定义普通函数的格式完全一致,只不过装饰器函数的参数必须要有函数或类对象。
  4. 然后再装饰器函数中重新定义一个新的函数或类,并且再其中执行某些功能前后使用被装饰的函数或类。
  5. 最后返回这个新定义的函数或类。
def func1(func): #func为被装饰的函数对象,符合“装饰器函数的参数必须要有函数对象或类对象”这一条件
    def func2(x,y): #func2()为再装饰函数中重新定义的一个函数,并返回了被装饰的函数func
        x += 1
        y += 2
        return func(x,y)
    return func2 #最后返回这个新定义的函数

@func1 #使用装饰器func1来装饰函数Sum
def Sum(a,b):
    print('结果为:', a + b)
    
Sum(1,2)

代码解读

  1. 装饰器@func1其实可以看作func1(Sum)(a,b),它将函数Sum作为函数对象传给了func1,然后func1返回一个新定义的函数对象func2,故可以理解为func1(Sum) == func2
  2. Sum的参数a和b传给了func2的x和y,故又可以理解为func1(Sum)(a,b) == func2(a,b) == func2(x,y),之后发生新定义的函数func2的调用。
  3. 又新定义的函数返回的是被装饰函数func的调用,而此时的func就是实参Sum函数,故此处发生了Sum函数的调用,而调用的参数a和b来源于变化后的x和y,故装饰器使函数Sum加入了一些新的功能(这些功能包装在新定义的函数func2中)
  4. 综上可总结出下列关系:@func1 == func1(Sum)(a,b) == func2(a,b) == func2(x,y) == func(x,y)

运算结果

结果为: 6

参考文献

异步图书 // Python编程——从入门到精通 叶维忠编著 人民邮电出版社

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值