外部函数能修改闭包内的变量_Python函数详解二(闭包、装饰器)

本文详细介绍了Python中的闭包概念,包括其定义、条件以及如何通过内部函数引用外部变量。同时,讨论了闭包变量的修改方式,如使用`nonlocal`关键字和修改可变类型数据。接着,文章探讨了装饰器的原理,展示了如何利用闭包特性创建装饰器以在不改变原有函数代码的情况下添加额外功能,如计算函数运行时间。最后,提到了装饰器带参数的应用场景。

277fa2b1e86e35e860219d9ad45703a8.png

闭包

闭包其实利用了函数嵌套的概念,一般函数在内部定义一个变量,在外部由于作用域的关系是调用不到的,而闭包是将变量包起来,不管在哪里都可以调用的到。

函数的嵌套定义:函数内定义了另外一个函数

# 示例1name='Alice'def outer():    def inner():        print(name)    print(inner.__closure__)    return inner

其中outer称为外部函数,inner称为内部函数。

对于一个函数,outer是其函数名,outer()为函数的调用,python中函数名可以用做函数的参数也可以作为函数的返回值。

那么什么是闭包呢?

闭包满足的三个条件:

1. 必须是嵌套函数;

2. 内部函数必须引用外部函数中的变量

3. 外部函数返回了内部函数或者外部函数调用了内部函数

查看是否为闭包:__closure__

#示例2def outer():    name = 'Alice'    def inner():        print(name)    print(inner.__closure__)    #(,)    return inner

可以试一下示例1的__closure__值,值是None,所以示例1并不是闭包,原因是内部函数没有引用外部函数的变量。

内部函数的调用

f=outer()    #调用外部函数,返回内部函数并存给ff()    #调用内部函数

注意:

内函数不能直接被调用。

闭包中被内部函数引用的变量,不会因为外部函数结束而被释放掉,而是一直存在内存中,直到内部函数被调用结束。

闭包变量

局部变量的作用域在函数内部,如果内部函数想修改外部函数的变量,有两种方法:

1.将变量声明为nonlocal,这表明变量不仅在函数内部生效,还在上一级函数中生效

def outer():    name = 'Alice'    def inner():        nonlocal name        name='Haha'        print(name)    return innerf=outer()f()

2.把闭包变量改成可变类型数据进行修改,比如列表。

def outer():    name = ['Alice']    def inner():        name=['Haha']        print(name[0])    return innerf=outer()f()

注意:

1.nonlocal关键字只能用于嵌套函数中,并且外部函数中定义了相应的局部变量

2.外函数被调用一次返回了内函数,因此实际上闭包变量只有一份,以后每次调用内函数,都使用同一份闭包变量,一旦在内部函数修改了闭包变量,则这个闭包变量的值就已经修改了,不是最初的值了。

装饰器

装饰器本质上是一个函数,使用了闭包的特性,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。

现有函数:

def sleep_f():    time.sleep(3)

如果要计算该函数的运行时间,代码如下

start_time=time.time()sleep_f()end_time=time.time()print("程序运行了{0:.4f}秒".format(end_time-start_time))

缺点:

1.调用sleep_f()依旧无法打印运行时间。

2.如果还有其他很多函数要显示运行时间,同样的代码得写很多遍。

解决方法:使用装饰器。

定义装饰器如下:

def timer(func):    def inner(*args, **kwargs):        start_time=time.time()        func(*args, **kwargs)        end_time=time.time()        print("程序运行了{0:.4f}秒".format(end_time-start_time))    return inner

装饰器装饰函数的方法:函数的上一行加 @装饰器名,其中,@符号是装饰器的语法糖

装饰器使用的两种方法:

装饰器不带参数

# 被装饰函数不带参数@timerdef sleep_f():    time.sleep(3)#调用sleep_f,这时候就会打印运行时间了sleep_f()
# 被装饰函数带参数@timerdef add(a,b):    print(a+b)    time.sleep(2)add(2,3)

装饰器带参数

timer装饰器打印了函数的运行时间,如果还想打印日志,即想要装饰器含参数,需要在timer的外层再装饰一层函数

def flog(name):    def timer(func):        def inner(*args, **kwargs):            print("调用{}模块的{}函数".format(name,inner.__name__))            start_time=time.time()            func(*args, **kwargs)            end_time=time.time()            print("程序运行了{0:.4f}秒".format(end_time-start_time))        return inner    return timer
@flog('module_a')def add(a,b):    print(a+b)    time.sleep(2)# 调用add(6,7)

推荐阅读

Python函数详解一(函数参数、变量作用域)

18ff70021cc34d447acea0d5ac4d19e3.png

539f36afe8420ca0fbd4f18e36582f42.png你点的每个好看,我都认真当成了喜欢
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值