內層函數對外層函數,非全局變數的引用。 判斷閉包函數:函數名.__closure__(),返回的值中有cell這個元素表示該函數為閉包函數。 閉包函數的機制:當函數開始執行時,如果遇到了閉包,在記憶體當中會為閉包開闢一個記憶體空間,用於將閉包中的變數等值放入其中,並不會隨著函數的執行完畢而消失。 ...
一、【函數名】
1)函數名本質上它也是一種變數,特殊的變數;
(碰到同名其它變數,依照從上往下的代碼執行賦值。)
單獨列印函數名,輸出的是它對應的記憶體地址:
例:
def funcl():
print(666)
print(funcl)
返回如下:
<function funcl at 0x0000000000DF5488>
2)函數名可以賦值給其它變數名;
def funcl():
print(666)
f1 = funcl
f1()
666
3)函數名可以作為容器類的元素;
def funcl():
print(666)
# print(funcl)
f1 = funcl
f1()
def f2():
print(222)
def f3():
print(333)
def f4():
print(444)
li =[f1, f2, f3, f4]
print(li)
# 返回的是對應的記憶體地址
for i in li:
i()
# 依次執行函數,可以看到函數名作為列表(容器)的元素也是可以的。
666
222
333
444
4)函數名可以作為參數;
def f1():
print(666)
def f2(a):
a()
print(777)
f2(f1)
輸出的結果:
666
777
上例中f1函數名是作為f2函數的參數。
5)函數名可以作為函數的返回值。
例:
def f1():
print(666)
def f2():
return f1
f2()()
# 輸出666
def f3():
def f4():
print(777)
return f4
f3()()
#輸出 777
以上例子可以看出函數名也可以作為函數的返回值,而且通過return返回的嵌套函數可以調用執行成功。
像上例中的f3()()如果改成f4()是會報錯:NameError: name 'f4' is not defined,原因就是全局命名空間中找不到f4()函數定義。所以這是命名空間很神奇的一個地方。
二、【閉包函數】
內層函數對外層函數,非全局變數的引用。
判斷閉包函數:函數名.closure(),返回的值中有cell這個元素表示該函數為閉包函數。
閉包函數的機制:當函數開始執行時,如果遇到了閉包,在記憶體當中會為閉包開闢一個記憶體空間,用於將閉包中的變數等值放入其中,並不會隨著函數的執行完畢而消失。
函數內部定義的函數稱為內部函數
主要作用有:
1)緩存,節省記憶體空間;例如爬蟲用,(不斷重覆爬取同一網頁的情況下)
2)裝飾器,最能完整體現出閉包的作用。
由於作用域的關係,
我們不能直接拿到函數內部的變數和函數了。如果我們就是想拿怎麼辦呢?通過返回值!
函數內的變數要想在函數外部用,可以直接返回這個變數,那麼如果想在函數外部調用函數內部的函數呢?
是不是直接就把這個函數的名字返回就好了?
這才是閉包函數最常用的用法。所以這也是裝飾器之所以是閉包函數最能完整體現用法的原因。
例:閉包函數的運用,引用外層的函數。
def func():
name = 'eva'
def inner():
print(name)
return inner
func() # 直接執行func,並沒有調用到func裡面的inner函數,不會輸出eva
f = func()
f() # 相當於是執行func()(),而func()中返回了inner函數,
# 所以又相當於是在func函數體內部中執行func(inner),所以就能正常執行了。
#輸出
eva
例:判斷是否為閉包函數
函數名.__closure__()
#輸出的__closure__有cell元素 :是閉包函數
def func():
name = 'eva'
def inner():
print(name)
print(inner.__closure__)
return inner
f = func()
f()
print('我是華麗的分割線'.center(30, '-'))
#輸出的__closure__為None :不是閉包函數(沒有引用外層,試圖引用的是全局的變數,所以不為閉包)
name = 'egon'
def func2():
def inner():
print(name)
print(inner.__closure__)
return inner
f2 = func2()
f2()
(<cell at 0x0000000000BF9138: str object at 0x0000000000BE0D18>,)
eva
-----------我是華麗的分割線-----------
None
egon
例:閉包的嵌套(兩層,還可以嵌套到三層,一般三層己夠滿足需要了)
下麵是藉助不斷給函數名賦值成變數來調用嵌套的函數的,這個方法其實在說到裝飾器時會用上,為什麼要簡化成一個變數名()的方式,就是為了裝飾其他函數用(不用改變其他函數的表現形式)
def wrapper():
money = 1000
def func():
name = 'eva'
def inner():
print(name, money)
return inner
return func
f = wrapper()
i = f()
i()
eva 1000
例:閉包函數獲取網路應用(簡單爬蟲雛形)
下例為我爬取我的個人wordpress博客首頁所用的簡單代碼,
到最後的內容需要用到decode解碼才能看到頁面源代碼。
還可以更進一步使用文件操作寫入到文件。
from urllib.request import urlopen
def index():
url = "https://www.tielemao.com"
def get():
return urlopen(url).read()
return get
tielemao = index()
content = tielemao()
print(content.decode('utf-8'))
寫到文件上保存:
from urllib.request import urlopen
def index():
url = "https://www.tielemao.com"
def get():
return urlopen(url).read()
return get
tielemao = index()
content = tielemao()
# print(content.decode('utf-8'))
s = content.decode('utf-8')
with open('tielemao_index.html', encoding='utf-8', mode='a') as f1:
f1.write(s)
end
2018-4-3