python - 闭包

百度百科上的定义:

闭包就是能够读取其他函数内部变量的函数。只有函数内部的子函数才能读取局部变量,所以闭包可以理解成 “定义在一个函数内部的函数” 。
在本质上,闭包是将函数内部和函数外部连接起来的桥梁。


举个栗子,例子1

def sum(a):
    def add(b):
        return a + b
    return add

num = sum(2)
print(num)
print(type(num))
print(num.__name__)  # 打印函数的名称,从结果可以看出,返回函数add

# 运行结果
<function sum.<locals>.add at 0x0000000001E11048>
<class 'function'>
add

解析说明

如果再一个函数内部嵌套了另外一个函数(将外部的函数和内部的函数成为外函数和内函数),函数中引用了外函数中的变量,并且外函数的返回值是内函数,称之为 闭包。


上面的列子:
sum 为外函数,add 为内函数;
内函数 add 引用了外函数 sum 的变量a;
外函数 sum 返回内函数 add;
符合闭包的定义。

普通函数

一般情况下,函数调用结束后,函数内定义的变量将不可使用。

# -*- coding:utf-8 -*-
def do_sth():
    temp = 9
    print(temp)

do_sth()  # 调用函数,打印 temp

print(temp)  # 会报错,提示temp没定义, NameError: name 'temp' is not defined

闭包

对于闭包而言,外函数调用结束后,外函数中被内函数引用的变量仍然可用,
因为外函数中被内函数引用的变量会被绑定到内函数的特殊属性__closure__中。

def outer():
    a = 9
    def inner():
        print(a)
    return inner

outer()() # 9

外函数中被内函数引用的变量会被绑定到内函数的特殊属性__closure__中。

def outer():
    a = 9
    def inner():
        print(a)
    return inner

result = outer()
print(result.__closure__)  # (<cell at 0x0000000001DEF978: int object at 0x000007FED6CF7D40>,) 是一个元祖
print(result.__closure__[0].cell_contents)  # 9
# closure:闭包
# cell_contents: 单元格内容

栗子2

def outer():
    a = 9
    def inner():
        a += 1
    return inner

outer()()
# UnboundLocalError: local variable 'a' referenced before assignment
# UnboundLocalError:赋值前引用的局部变量“a”

"""
a += 1 相当于 a = a + 1
重新定义一个变量 a, 把外函数中的变量 a 给屏蔽了;
当计算等号右边的 a + 1 时,新定义的变量 a 还没有被赋值,因此程序会报错
"""

栗子3

def outer():
    a = [5]  # a 为列表,列表是可变类型对象
    def inner():
        a[0] = 9  # 此时 a 没有波浪线了,修改列表 a 的对象的值,把列表的值改为 9

        print(a)
    return inner

outer()()  # [9]

对比例2和例3,得出的结论:

在默认情况下,在内函数中不能修改外函数中的变量引用的对象,
如果引用的对象是可变类型的,可以修改对象的内容。


如果想在内函数中修改外函数中的变量所引起的的对象,要怎么办?

def outer():
    a = 10  # a 为列表,列表是可变类型对象

    def inner():
        nonlocal a
        a += 1

        print(a)
    return inner


outer()()  # 11

如果想在内函数中修改外函数中的变量所引起的的对象,可以在内函数中使用关键字
nonlocal对变量进行声明,从而表明在内函数中并没有重新定义一个新的同名变量,
而是使用外函数中该名称的变量。



闭包经典面试题

1、下列程序是否是闭包,是否能正常运行

def outer():
    n = 2
    
    def inner(x):
        n += 1
        return x ** n
    return inner

p = outer()
print(p(3))

解析:是闭包,但是运行会报错

修改:

def outer():
    n = 2

    def inner(x):
        nonlocal n
        n += 1
        return x ** n
    return inner

p = outer()
print(p(3))  # 27

2、以下程序执行的结果是多少

def outer():
    n = 2
    L = []
    for i in range(1, 3):
        def inner():
            return i ** n
        L.append(inner)
    return L


f1, f2 = outer()
print(f1())  # 4
print(f2())  # 4

python的函数只有在执行时,才会去找函数体里的变量的值,也就是说你连形参都不确定,
你咋知道i为几呢?,在这里,你只需要记住如果你连形参都不确定,python就只会记住最后一个i值。



------分界线------

* 学到知识
* 积累经验
* 太高思维
* 锻炼心性 
* ......

------

有了这个思维,

你自然会变得更好,
结果就自然会变好,
钱自然会来找你,

posted @ 2021-10-21 19:18  西瓜_皮  阅读(112)  评论(0编辑  收藏  举报