python學習 day011打卡 迭代器
本節的主要內容:
1.函式名的使用以及第一類物件
2.閉包
3.迭代器
一.函式名的運用.
函式名是一個變數,但它是一個特殊的變數,與括號配合可以執行函式的變數.
1.函式名的記憶體地址
def func(): print("呵呵") print(func) 結果: <function func at 0x1101e4ea0>
2.函式名可以賦值給其他變數
def func(): print("呵呵") print(func) a = func # 把函式當成一個變量賦值給另⼀個變量 a() # 函式調⽤用 func()
3.函式名可以當做容器類的元素
def func1(): print("呵呵") def func2(): print("呵呵") def func3(): print("呵呵") def func4(): print("呵呵") lst = [func1, func2, func3] for i in lst: i()
4.函式名可以當做函式的引數
def chi(fn): # fn 代理了func1和func2 print("開掛") fn()print(fn.__name__) print("洗包") def play_dnf(): print("瘋狂的刷") def func1(): print("我是func1") def func2(): print("我是func2") def he(): print("我要喝酒") chi(he)
代理模式
裝飾器的雛形
把函式名當成變數來使用
5.函式名可以作為函式的返回值
def outer(): def inner(): print("哈哈") return inner outer()()
二.閉包
閉包就是內層函式,對外層函式(非全域性)的變數的引用,叫閉包
def func1(): name = "alex" def func2(): print(name) # 閉包 func2() func1() 結果: alex
我們可以使用__closure__來檢測函式是否是閉包.使用函式名.__closure__返回cell就是閉包.返回None就不是閉包
def outer(): # a = 10 def inner(): print("哈哈") print(inner.__closure__) # (<cell at 0x000001C079677588: int object at 0x0000000054776D30>,) return inner
問題:如何在函式外邊呼叫內部函式呢?
def outer(): name = "alex" # 內部函式 def inner(): print(name) return inner fn = outer() # 訪問外部函式, 獲取到內部函式的函式地址 fn() # 訪問內部函式
那如果多層巢狀呢?很簡單隻需要一層一層的返回就行了
def func1(): def func2(): def func3(): print("嘿嘿") return func3 return func2 func1()()()
有它我們可以引出閉包的好處.由於我們在外界可以訪問內部函式.那這個時候內部函式訪問的時間和時機就不一定了,因為在外部,我
可以選擇在任意的時間去訪問內部函式.這個時候.想一想.我們之前說過,如果一個人函式執行完畢.則這個函式中的變數以及區域性名稱空間
中的內容都將會被銷燬.在閉包中,如果變數被銷燬了.那內部函式將不能正常執行.所以,python規定,如果你在內部函式中訪問了外層函式
的變數.name這個變數將不會消亡.將會常駐在記憶體中.也就是說,使用閉包,可以保證外層函式中的變數在記憶體中常駐.這樣做有什麼好處呢?
非常大的好處.我們來看一個關於爬蟲的程式碼:
from urllib.request import urlopen # 開啟一個連線用的模組 # 外層函式 def but(): # 開啟連線. 讀取原始碼 content = urlopen("http://www.cctv.com/").read() # 永久駐留在記憶體 # 內層函式 def get_content(): # 返回content return content return get_content # 內層函式 fn = but() # 這裡會很慢. 需要網路請求 print(fn()) # 不會再進行網路請求了 print(fn())
綜上,閉包的作用就是讓一個變數能夠常駐記憶體,供後面的程式使用
閉包的作用:
1.保護我們的變數不受侵害
2.可以讓一個變數常駐記憶體
三.迭代器
我們之前一直在用可迭代物件進行迭代操作,那麼到底什麼是可迭代物件?
我們熟悉的可迭代物件有:
str,list,tuple,dict,set.那為什麼我們可以稱他們為可迭代物件呢?因為他們都遵循了可迭代協議.
我們可以通過dir函式來檢視類中定義好的所有辦法.
print(dir(str)) # 檢視str能夠執行的操作. 內部的方法
在列印結果中,尋找__iter__如果能找到,那麼這個類的物件就是一個可迭代物件
我們發現這幾個可以進行for迴圈的東西都有__iter__函式,包括range也有.可以自己試一下.
這是檢視一個物件是否是可迭代物件的第一種辦法.我們還可以通過isinstence()函式來檢視一個物件是什麼型別
l = [1,2,3] l_iter = l.__iter__() from collections import Iterable from collections import Iterator print(isinstance(l,Iterable)) #True print(isinstance(l,Iterator)) #False print(isinstance(l_iter,Iterator)) #True print(isinstance(l_iter,Iterable)) #True
# collections 關於集合類的相關操作
# Iterable : 可迭代的
# Iterator : 迭代器
綜上.我們可以確定,如果物件中有__iter__函式.那麼我們認為這個物件遵守了可迭代協議.
就可以獲取到相應的迭代器.這裡的__iter__是幫助我們獲取到物件的迭代器.我們使用迭代器中的__next__()
來獲取到一個迭代器中的元素.那麼我們之前將的for的工作原理到底是什麼?繼續看程式碼
s = "我愛北京天安門" c = s.__iter__() # 獲取迭代器器 print(c.__next__()) # 使用迭代器器進行迭代. 獲取一個元素 我 print(c.__next__()) # 愛 print(c.__next__()) # 北 print(c.__next__()) # 京 print(c.__next__()) # 天 print(c.__next__()) # 安 print(c.__next__()) # 門 print(c.__next__()) # StopIteration
for迴圈的機制:
for i in [1,2,3]: print(i)
使用while迴圈+迭代器來模擬for迴圈(必須要掌握)
lst = [1,2,3] lst_iter = lst.__iter__() while Ture : try : i = lst_iter.__next__() print(i) except StopIteration : break
總結:
iterable:可迭代函式
物件.內部包含__iter__()
Iterator:迭代器.內部包含__iter__()同時包含__next__().
迭代器的特點:
1.節省記憶體.
2.惰性機制.
3.不能反覆,只能向下執行.
我們可以把要迭代的內容當成子彈.然後呢.獲取到迭代器__iter__().就把子彈都裝在單價中.然後發射就是__next__()把每一個子彈(元素)打出來.
也就是說,for迴圈的時候.一開始的時候是__iter__()來獲取迭代器.很後面每次獲取元素都是通過該__next__()來完成的.當程式遇到StopIteration將結束迴圈.