前言
前面學了裝飾器,那么閉包和裝飾器有什么區別呢?
閉包傳遞的是變量,而裝飾器傳遞的是函數對象,只是傳的參數內容不一樣,閉包的概念包含了裝飾器,可以說裝飾器是閉包的一種,它只是傳遞函數對象的閉包。
先看一個面試題
先看一個經典的面試題,很有代表性, 運行以下代碼會輸出什么呢?為什么會是這種結果?
def fun():
temp = [lambda x: i*x for i in range(4)]
return temp
for everyLambda in fun():
print(everyLambda(2))
運行結果
6
6
6
6
運行的結果是4個6 ,並不是我們想的 :0, 2, 4, 6。上面的代碼用到了列表推導式,還有個匿名函數lambda,直接去閱讀不太好理解,可以把匿名函數轉成自己定義一個函數.
於是上面的代碼等價於:
"""
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
def fun():
temp = [lambda x: i*x for i in range(4)]
return temp
for everyLambda in fun():
print(everyLambda(2))
"""
def fun():
temp = []
for i in range(4):
def inner(x):
return i*x
temp.append(inner)
return temp
for everyLambda in fun():
print(everyLambda(2))
為了更好的理解,可以先去掉外面的一層fun()
temp = []
for i in range(4):
def inner(x):
return i*x
temp.append(inner)
for everyLambda in temp:
print(everyLambda(2))
這里只定義了一個函數 inner(), 有 2 個變量,i 是函數外部的變量,x 是函數內部的變量。
現在問題的關鍵在理解函數外部變量和函數內部變量的區別了, 接下來再看一個簡單的例子
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
a = 1
def myfunc(b):
return a+b
print(myfunc(100))
a = 2
a = 3
print(myfunc(100))
print(myfunc(100))
運行結果:101 103 103
也就是函數外部變量a是可變的,后面給a重新賦值了,會替換前面的值。上面的 inner(x) 函數也是一樣,外部變量 i 的值是0, 1, 2, 3變化,最后的3 會覆蓋前面的值,所以得到的結果都是6
如何解決上面的問題,接下來就是要說的閉包的概念了!
什么是閉包?
閉包就是外部函數中定義了一個內部函數,當外部函數返回內部函數對象(注意是函數對象)時,程序接收了內部函數的定義(此時並未被執行),當再次執行這個返回值時,這個被返回的函數才能被執行。
創建一個閉包必須滿足以下幾點:
- 必須有一個內嵌函數
- 內嵌函數必須引用外部函數中的變量
- 外部函數的返回值必須是內嵌函數
閉包和裝飾器的區別:閉包傳遞的是變量,而裝飾器傳遞的是函數,除此之外沒有任何區別,或者說裝飾器是閉包的一種,它只是傳遞函數的閉包。
以下是閉包的一個標准示例:
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
def outer(age):
def inner(name):
print("my name is %s. my age is %s." % (name, age))
return inner
demo = outer("18")
demo("yoyo")
# 運行結果:my name is yoyo. my age is 18.
上面的問題,用閉包來解決
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
# 閉包
def temp(i): # 一個外層函數
def inner(x): # 內層函數
return i*x
return inner
a = []
for i in range(4):
a.append(temp(i))
print(a)
for j in a:
print(j(2))
運行結果
[<function temp.<locals>.inner at 0x000002A8EE929AE8>,
<function temp.<locals>.inner at 0x000002A8EE929B70>,
<function temp.<locals>.inner at 0x000002A8EE929BF8>,
<function temp.<locals>.inner at 0x000002A8EE929C80>]
0
2
4
6
使用列表推導式
# 閉包
def temp(i): # 一個外層函數
def inner(x): # 內層函數
return i*x
return inner
def fun():
temps = [temp(i) for i in range(4)]
return temps
for everyLambda in fun():
print(everyLambda(2))
這樣就可以得到我們的預期結果:0 2 4 6
通過上面的案例就可以了解到閉包的作用了,它保存了函數的外部變量,不會隨着變量的改變而改變了。
