Python 闭包,生成式,推导式

闭包概念

闭包,又称闭包函数或者闭合函数,其实和前面讲的嵌套函数类似,
不同之处在于,闭包中外部函数返回的不是一个具体的值,而是一个函数。一般情况下,返回的函数会赋值给一个变量,这个变量可以在后面被继续执行调用。
内部函数使用了外部函数变量或参数,且外部函数返回了内部函数,则将这个使用了外部函数变量的内部函数称为闭包

闭包作用的特点如下

可以保存外部函数内的变量,不会随外部函数调用完而销毁
闭包可以提高代码的可重用性,无需再手动定义额外的功能函数
由于闭包引用了外部函数变量,外部函数变量没有及时释放,消耗内存


def test(x):
    return x


def outter(func):
    y = "你追我,"

    def inner1():
        nonlocal y
        print(y, end="")
        ret = func("如果你追到我,")
        print(ret, end="")
        return "我就让你嘿嘿嘿"

    return inner1


ret = outter(test)()
print(ret)
------------------------
def outter():
    name = "test"
    def inner():
        print("from inner,name is%s" % name)
        return "inner函数"
    return inner
a = outter()
print(a.__name__)
print(a())

----------------
运行结果如下
inner
from inner,name istest
inner函数

以上就是个闭包函数的典型示例,有如下特征:

  1. 一个外部函数outter内部会包裹多个inner子函数,因为函数的作用域关系,对inner函数而言,outer函数的name相对来说就是全局变量,
  2. inner 函数结束后 必须有个return inner 进行函数返回
    outter的返回值指示inner函数的内存地址

闭包应用场景

闭包的实际应用场景更多在于异步调用(协程和多线程的异步).因为异步调用,函数每次执行完成需要的时间不同,有些变量无法从全局传入.最好的方式就是讲参数执行的时候就传入闭包中,因为作用域的关系.
局部变量彼此之间不会相互影响.

上面例子中 add_done_callback方法会再线程池调用完down函数以后,继续调用done函数,但是此时只能传入一个arg参数,里面的arg.result()就是前面down函数的返回值.
此时无法传入filename参数,也无法设置全局变量,因为我们要实现保存的文件名是不同的.

解决办法

办法1: 我们把filename这个参数也写入down的返回值中

办法2: 就是使用闭包函数,将filename参数传入闭包outter函数,在其outter内部作为全局变量,就可以将闭包体内将filename传入.
而且outter函数运行的时候会单独创建一个独立的内存栈.多线程并发运行的三个outter函数是作用与不同的内存地址的.所以他们内部的局部变量也是各自独立.

生成式和表达式的区别

明显的区别是生成式是(代码块),而表达式是[代码块]

用下面经典案例描述运作模式

def num():
    return [lambda x: x * i for i in range(4)]

ret = [i(2) for i in num()]
print(ret)

输出结果是[6,6,6,6]

上面是表达式内用匿名函数生成的案例.
表达式的特点是遍历循环完毕后,再进行执行.
for i in num() 这句会先执行num函数,num执行结果返回的是一个列表,里面有4个匿名函数体,注意此时匿名函数体的x*i是不执行的,所以之前i再遍历循环传入的值是无效的.而for循环完毕后I就是为3.
因此num返回的列表可以理解为4个匿名函数被放入了一个列表.
接下来是[i(2) for i in num()]对4个匿名函数遍历循环 i就是匿名函数 i(2) = lammbda x:2*i,而匿名函数此时的i 就是上面循环完毕后的3.所以4个匿名函数都是计算结果都是2*3

-------------------------------

接下来是生成式,可以看到2个例子就是()和[]的区别,但是计算方式天差地别

def num():
    return (lambda x: x * i for i in range(4))

ret = [i(2) for i in num()]
print(ret)

第一步一样,还是运行num函数,生成式的特点是生成一个,使用一个,前一个不使用,后面的就不生成.以此实现内存节约目的.
虽然都是运行num函数但是此时不会一次性生成4个,而是只生成一个匿名函数,
此时匿名函数的i=0 而x则会再i(2)执行的时候传入2,所以第一个返回值是0
第二个 匿名函数的i为1 而i(2)传入的x为2 返回值为2
第三个 匿名函数的i为2 而i(2)传入的x为2 返回值为4
第四个 匿名函数的i为3 而i(2)传入的x为2 返回值为6

生成式的斐波那契数列的示例


def fib(max_count):
    first = 1
    second = 0
    count = 0
    while count<max_count:
        nex_val = second+first
        first,second = second,second+first
        yield nex_val
        count+=1

for i in  fib(5):
    print(i)

迭代器 和生成器

理论知识

迭代器 内部有__iter__和__next__方法 前者返回自身,后者可以获取数据,当数据获取完毕后抛出Stopiterration的异常
生成器 函数中有yield就是生成器函数,执行函数返回的就是生成器对象(特殊的迭代器),可以通过next方法取值
可迭代对象 内部有__iter__方法且返回一个迭代器对象,可以被for循环遍历

posted @ 2022-12-14 17:01  零哭谷  阅读(54)  评论(0编辑  收藏  举报