作为一个没有女朋友的码农,情人节这天只能敲敲代码,或者写写一些自己学习上的一些心得
下面我就说一说我学习“闭包” 这个结构时的一些问题和心得吧!
刚接触闭包的时候,感觉闭包很简单啊,在Python 中,不就是一个函数内再定义一个函数么,里面的函数用了外面函数的变量,这有什么难的?可随着不断的深入学习和研究,才发现了闭包结构的魅力。
首先学习闭包之前,你必须有一些Python 基础的了解,最重要的一点,就是函数名的概念,不了解的同学看下面这个例子:
def test1():
print("in test1")
test1() # in test1
f = test1
f() # in test1
分析一下,首先我们定义了一个函数 test1, 函数的作用就是打印一句话“in test1”,接着我们调用了一下这个函数,发现没有什么问题,能正常打印。
接下来的就比较奇怪了,我们把test1 这个函数名赋值给了一个变量f (赋值这个词用的不是很恰当),然后对f 进行了函数调用,发现能打印出和调用test1 一样的内容,是不是很奇怪,我们并没有定义f 这个函数。对比以下小栗子,可能你就明白其中的原因了。
a = 10
b = a
print(b) # 10
第一行我们先创建了变量a,并把数值10 赋值给它,也就是数值10 的引用加一, 然后第二行是把变量a 赋值给了变量b,Python 中的赋值就是引用的传递 ,也就是将变量a 引用的内容 传递给了变量b,此时数值10 的引用再次加一,所以会有了第三行的结果,我们并没有直接把数值10 赋值给变量b,而是通过变量a 的引用传递,使得变量b 也被赋值了10.
说了那么多,大家有没有发现什么问题?没错,函数名test1 不就相当于 变量a 吗? f 不就相当于变量b 吗?不同的是 test1 引用的是一个函数代码空间,而a 引用的是一个整数10,那么是不是就可以说函数和变量名一样,可以把它引用的代码空间传递给其他变量名? 当然是这样的,如果不成立的话,可能就不会有类似闭包这个结构了。
接下来,我们再根据下面的例子,来继续学习闭包
def outer(number_out):
def inner(number_in):
return number_out + number_in
return inner
f1 = outer(100)
f2 = outer(100)
print(f1) # <function outer.<locals>.inner at 0x000002102F5E02F0>
print(id(f1)) # 2268537422576
print(id(f2)) # 2268537422440
分析f1 = outer(100) ,这个100 其实就是number_out 的实参,并且调用返回了 inner 这个函数名,结果为 f1 = inner,通过引用传递,将内部定义的函数 inner所引用的函数代码块,传递给了f1,此时f1 是一个 函数对象,但为什么使用相同函数调用,传入的实参也一样,两个对象却不一样?这是因为f1 = inner 的这个结果,是只有在调用outer(number_out) 的时候才去定义的inner 这个函数,作用域不同,也就不会是相同的inner 了,这也就是闭包的魅力之一了,此时调用f1(number_in) 和调用f2(numberi_in) 之间互不影响。
如果觉得上面的例子还算简单,那么再看一下稍微复杂一点的例子:
def line_conf(a, b):
def line(x):
return a * x + b
return line
line1 = line_conf(1, 2)
line2 = line_conf(3, 3)
print(line1(3)) # 5
print(line2(3)) # 12
上面这个闭包的例子,函数line 和 变量a,b 构成闭包,通过确定line_conf 的参数a,b,转换成实际的函数就是,line1 :y = x + 2,line2:y = 3x + 3,所以我们只需要变换参数a,b 的值,就能得到不同的一次函数表达式,如果没有闭包结构,每次都要传递a, b, x 三个参数,减少了代码的可移植性,由此可以看出,闭包结构具有提高代码复用性的作用。
注意:由于闭包引用了外层函数的局部变量,则外层函数的局部变量没有及时释放,消耗内存
总结:
闭包结构就是一个嵌套定义的函数,在外层函数开始的时候才开始定义内层函数的定义,然后将内层函数代码块的引用传递给函数外的对象
内层函数和使用外层函数的提供的变量所构成的整体就被称为闭包
扩展:闭包结构中,修改外层函数局部变量的方法
Python 2
def counter(start=0):
count = [start]
def inner():
count[0] += 1
return count[0]
return inner
f1 = counter()
f2 = counter()
print(f1()) # 1
print(f1()) # 2
print(f2()) # 1
print(f2()) # 2
由此也可以看出,f1 和f2 确实是互不影响的两个对象.
Python 3
def counter(start=0):
def inner():
nonlocal start
start += 1
return start
return inner
f1 = counter()
f2 = counter()
print(f1()) # 1
print(f1()) # 2
print(f2()) # 1
print(f2()) # 2