您現在的位置是:首頁 > 遊戲
Python 迭代器與生成器
什麼是生成器
迭代
可以被for迴圈的就是可迭代的,目前有字串,列表,元組,字典,集合
from collections import Iterable
l = [1, 2, 3, 4]
t = (1, 2, 3, 4)
d = {1: 2, 3: 4}
s = {1, 2, 3, 4}
print(isinstance(l, Iterable))
print(isinstance(t, Iterable))
print(isinstance(d, Iterable))
print(isinstance(s, Iterable))
結合使用for迴圈取值的現象,再從字面上理解一下,其實迭代就是剛剛說的,可以將某個資料集內的資料“一個挨著一個的取出來”,就
叫做迭代
。
可迭代協議
現在是從結果分析原因,能被for迴圈的就是“可迭代的”,但是如果正著想,for怎麼知道誰是可迭代的呢?
假如自己寫了一個數據型別,希望這個資料型別裡的東西也可以使用for被一個一個的取出來,那我們就必須滿足for的要求。這個要求就叫做“
協議
”。
可以被迭代要滿足的要求就叫做可迭代協議。可迭代協議的定義非常簡單,就是內部實現了__iter__方法。
print(dir([1,2]))
print(dir((2,3)))
print(dir({1:2}))
print(dir({1,2}))
[‘__add__’, ‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__delitem__’,
‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’,
‘__getitem__’, ‘__gt__’, ‘__hash__’, ‘__iadd__’, ‘__imul__’, ‘__init__’,
‘__init_subclass__’, ‘__iter__’, ‘__le__’, ‘__len__’, ‘__lt__’, ‘__mul__’,
‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__reversed__’,
‘__rmul__’, ‘__setattr__’, ‘__setitem__’, ‘__sizeof__’, ‘__str__’,
‘__subclasshook__’, ‘append’, ‘clear’, ‘copy’, ‘count’, ‘extend’,
‘index’, ‘insert’, ‘pop’, ‘remove’, ‘reverse’, ‘sort’]
[‘__add__’, ‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’,
‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__getitem__’,
‘__getnewargs__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’,
‘__iter__’, ‘__le__’, ‘__len__’, ‘__lt__’, ‘__mul__’, ‘__ne__’, ‘__new__’,
‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__rmul__’, ‘__setattr__’,
‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘count’, ‘index’]
[‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__delitem__’, ‘__dir__’,
‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__getitem__’,
‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’, ‘__iter__’, ‘__le__’,
‘__len__’, ‘__lt__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’,
‘__repr__’, ‘__setattr__’, ‘__setitem__’, ‘__sizeof__’, ‘__str__’,
‘__subclasshook__’, ‘clear’, ‘copy’, ‘fromkeys’, ‘get’, ‘items’, ‘keys’,
‘pop’, ‘popitem’, ‘setdefault’, ‘update’, ‘values’]
[‘__and__’, ‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’,
‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’,
‘__iand__’, ‘__init__’, ‘__init_subclass__’, ‘__ior__’, ‘__isub__’, ‘__iter__’,
‘__ixor__’, ‘__le__’, ‘__len__’, ‘__lt__’, ‘__ne__’, ‘__new__’, ‘__or__’, ‘__rand__’,
‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__ror__’, ‘__rsub__’, ‘__rxor__’,
‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__sub__’, ‘__subclasshook__’, ‘__xor__’,
‘add’, ‘clear’, ‘copy’, ‘difference’, ‘difference_update’, ‘discard’,
‘intersection’, ‘intersection_update’, ‘isdisjoint’, ‘issubset’,
‘issuperset’, ‘pop’, ‘remove’, ‘symmetric_difference’,
‘symmetric_difference_update’, ‘union’, ‘update’]
PS:注意
print(dir(‘’)) # dir()方法是檢視括號內的成分的全部方法
結果
[‘__add__’, ‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__dir__’, ‘__doc__’,
‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__getitem__’,
‘__getnewargs__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’,
‘__iter__’, ‘__le__’, ‘__len__’, ‘__lt__’, ‘__mod__’, ‘__mul__’, ‘__ne__’,
‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__rmod__’, ‘__rmul__’,
‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘capitalize’,
‘casefold’, ‘center’, ‘count’, ‘encode’, ‘endswith’, ‘expandtabs’, ‘find’,
‘format’, ‘format_map’, ‘index’, ‘isalnum’, ‘isalpha’, ‘isdecimal’, ‘isdigit’,
‘isidentifier’, ‘islower’, ‘isnumeric’, ‘isprintable’, ‘isspace’, ‘istitle’,
‘isupper’, ‘join’, ‘ljust’, ‘lower’, ‘lstrip’, ‘maketrans’, ‘partition’,
‘replace’, ‘rfind’, ‘rindex’, ‘rjust’, ‘rpartition’, ‘rsplit’, ‘rstrip’,
‘split’, ‘splitlines’, ‘startswith’, ‘strip’, ‘swapcase’, ‘title’, ‘translate’,
‘upper’, ‘zfill’]
總結
現在所知道的:可以被for迴圈的都是可迭代的,要想可迭代,內部必須有一個__iter__方法。
__iter__方法作用
print([1,2]。__iter__())
# 結果
執行了list([1,2])的__iter__方法,我們好像得到了一個list_iterator,現在我們又得到了一個新名詞——iterator。
iterator,是一個計算機中的專屬名詞,叫做迭代器。
迭代器協議
既什麼叫“可迭代”之後,又一個歷史新難題,什麼叫“迭代器”?
雖然不知道什麼叫迭代器,但是現在已經有一個迭代器了,這個迭代器是一個列表的迭代器。
這個列表的迭代器比起列表來說實現了哪些新方法,這樣就能揭開迭代器的神秘面紗了吧?
‘’‘
dir([1,2]。__iter__())是列表迭代器中實現的所有方法,dir([1,2])是列表中實現的所有方法,
都是以列表的形式返回給我們的,為了看的更清楚,我們分別把他們轉換成集合,
然後取差集。
’‘’
#print(dir([1,2]。__iter__()))
#print(dir([1,2]))
print(set(dir([1,2]。__iter__()))-set(dir([1,2])))
結果:
{‘__length_hint__’, ‘__next__’, ‘__setstate__’}
迭代器的三種方法
iter_l = [1,2,3,4,5,6]。__iter__()
#獲取迭代器中元素的長度
print(iter_l。__length_hint__())
#根據索引值指定從哪裡開始迭代
print(‘*’,iter_l。__setstate__(4))
#一個一個的取值
print(‘**’,iter_l。__next__())
print(‘***’,iter_l。__next__())
使用迭代器next方法來寫一個不依賴for的遍歷
l = [1,2,3,4]
l_iter = l。__iter__()
item = l_iter。__next__()
print(item)
item = l_iter。__next__()
print(item)
item = l_iter。__next__()
print(item)
item = l_iter。__next__()
print(item) # 到此處是正確的,繼續向下執行則因迭代器中沒有元素而丟擲異常StopIteration告訴我們,列表中已經沒有有效的元素了
item = l_iter。__next__()
print(item)
防止丟擲異常的辦法,使用異常處理機制把異常處理掉
l = [1,2,3,4]
l_iter = l。__iter__()
while True:
try:
item = l_iter。__next__()
print(item)
except StopIteration:
break
我們就使用while迴圈實現了原本for迴圈做的事情,我們是從誰那兒獲取一個一個的值呀?是不是就是l_iter?好了,這個l_iter就是一個迭代器。
迭代器遵循迭代器協議:必須擁有__iter__方法和__next__方法。
PS:關於range是不是一個迭代器
print(‘__next__’ in dir(range(12))) #檢視‘__next__’是不是在range()方法執行之後內部是否有__next__
print(‘__iter__’ in dir(range(12))) #檢視‘__next__’是不是在range()方法執行之後內部是否有__iter__
from collections import Iterator
print(isinstance(range(100000000),Iterator)) #驗證range執行之後得到的結果不是一個迭代器
生成器
迭代器有兩種:一種是呼叫方法直接返回的,一種是可迭代物件透過執行iter方法得到的,迭代器有的好處是可以節省記憶體。
如果在某些情況下,我們也需要節省記憶體,就只能自己寫。我們自己寫的這個能實現迭代器功能的東西就叫生成器。
Python中提供的生成器
生成器函式:常規函式定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函式的狀態,以便下次重它離開的地方繼續執行
生成器表示式:類似於列表推導,但是,生成器返回按需產生結果的一個物件,而不是一次構建一個結果列表
生成器Generator
本質:迭代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)
特點:惰性運算,開發者自定義
生成器函式
一個包含yield關鍵字的函式就是一個生成器函式。yield可以為我們從函式中返回值,但是yield又不同於return,return的執行意味著程式的結束,呼叫生成器函式不會得到返回的具體的值,而是得到一個可迭代的物件。每一次獲取這個可迭代物件的值,就能推動函式的執行,獲取新的返回值。直到函式執行結束。
import time
def genrator_fun1():
a = 1
print(‘現在定義了a變數’)
yield a
b = 2
print(‘現在又定義了b變數’)
yield b
g1 = genrator_fun1()
print(‘g1 : ’,g1) #列印g1可以發現g1就是一個生成器
print(‘-’*20) #我是華麗的分割線
print(next(g1))
time。sleep(1) #sleep一秒看清執行過程
print(next(g1))
生成器有什麼好處呢?就是不會一下子在記憶體中生成太多資料
假如我想讓工廠給學生做校服,生產2000000件衣服,我和工廠一說,工廠應該是先答應下來,然後再去生產,我可以一件一件的要,也可以根據學生一批一批的找工廠拿。
而不能是一說要生產2000000件衣服,工廠就先去做生產2000000件衣服,等回來做好了,學生都畢業了。。。
def produce():
“”“生產衣服”“”
for i in range(2000000):
yield “生產了第%s件衣服”%i
product_g = produce()
print(product_g。__next__()) #要一件衣服
print(product_g。__next__()) #再要一件衣服
print(product_g。__next__()) #再要一件衣服
num = 0
for i in product_g: #要一批衣服,比如5件
print(i)
num +=1
if num == 5:
break
#到這裡我們找工廠拿了8件衣服,我一共讓我的生產函式(也就是produce生成器函式)生產2000000件衣服。
#剩下的還有很多衣服,我們可以一直拿,也可以放著等想拿的時候再拿
生成器監控檔案輸出
import time
def tail(filename):
f = open(filename)
f。seek(0, 2) #從檔案末尾算起
while True:
line = f。readline() # 讀取檔案中新的文字行
if not line:
time。sleep(0。1)
continue
yield line
tail_g = tail(‘tmp’)
for line in tail_g:
print(line)
計算移動平均值——預激生成的裝飾器
def init(func): #在呼叫被裝飾生成器函式的時候首先用next啟用生成器
def inner(*args,**kwargs):
g = func(*args,**kwargs)
next(g)
return g
return inner
@init
def averager():
total = 0。0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total/count
g_avg = averager()
# next(g_avg) 在裝飾器中執行了next方法
print(g_avg。send(10))
print(g_avg。send(30))
print(g_avg。send(5))
yield from
def gen1():
for c in ‘AB’:
yield c
for i in range(3):
yield i
print(list(gen1()))
def gen2():
yield from ‘AB’
yield from range(3)
print(list(gen2()))
列表推導式和生成器表示式
#讓我來啟動點東西吧
run_list=[‘run %s’ %i for i in range(10)] #列表解析
#別直接全啟動了,咱們一個一個來
run_you=(‘run %s’ %i for i in range(10))#生成器表示式
print(run_you)
print(next(run_you)) #next本質就是呼叫__next__
print(run_you。__next__())
print(next(run_you))
列表推導式和生成器表示式總結
把列表解析的[]換成()得到的就是生成器表示式
列表解析與生成器表示式都是一種便利的程式設計方式,只不過生成器表示式更節省記憶體
Python不但使用迭代器協議,讓for迴圈變得更加通用。大部分內建函式,也是使用迭代器協議訪問物件的。例如, sum函式是Python的內建函式,該函式使用迭代器協議訪問物件,而生成器實現了迭代器協議,所以,我們可以直接這樣計算一系列值的和
sum(x ** 2 for x in range(4))
而不用多此一舉的先構造一個列表
sum([x ** 2 for x in range(4)])
使用生成器優點
延遲計算,一次返回一個結果。也就是說,它不會一次生成所有的結果,這對於大資料量處理,將會非常有用。
提高程式碼可讀性
#我要學Python#
推薦文章
- 兩代瀏陽人的花炮傳承:母親認為做煙花“像是進黑社會”,兒子計算機系畢業後,將母親單位打造成知名煙花企業
承包母親工作的“城東煙花廠”20餘年,唐雲將它打造成頗有名氣的煙花集團...
- 故事:我是個女帝,醋精丞相總讓朕後宮起火怎麼辦?
顧清時站起來,手沈慕儀坐著的椅背上:“陛下可是本相放在手上呵護了五年的花,諸位既然想佔本相這個位置,就要拿個方案出來,日後怎麼照顧陛下...
- 教師招聘每日一練2022年(12月20日)
C項錯誤,按照《事業單位崗位設定管理試行辦法》第一部分中的任職條件規定,一級、二級職員崗位按照國家有關規定執行...