迭代器和生成器是函數中的一大重點,務必掌握,何為迭代?何為迭代器? ...
迭代器和生成器是函數中的一大重點,務必掌握,何為迭代?何為迭代器?
預習:
1、處理文件,用戶指定要查找的文件和內容,將文件中包含要查找內容的每一行都輸出到屏幕(使用生成器)
2、批量處理文件,用戶指定要查找的目錄和內容,將本層目錄下所有文件中包含要查找內容的每一行都輸出到屏幕
一、迭代器
for i in 50: print(i) #運行結果: # Traceback (most recent call last): # File "G:/python/python代碼/八月/day2 迭代器生成器/3迭代器.py", line 8, in <module> # for i in 50: # TypeError: 'int' object is not iterable
報錯:
TypeError: 'int' object is not iterable
類型報錯:'int'對象是不可迭代的 何為迭代?
iterable:可迭代的;迭代的;
可迭代的:從上面代碼可以簡單分析出能被for迴圈取值的就是可迭代,那麼我們就可以初步總結出可迭代的類型:str、list、tuple、set、dict
可迭代的 ——對應的標誌 擁有__iter__方法
print('__iter__' in dir([1,2,3])) #判斷一個變數是不是一個可迭代的
可迭代協議
可以被迭代要滿足的要求就叫做可迭代協議。可迭代協議的定義非常簡單,就是內部實現了__iter__方法。
二、迭代器
__iter__方法作用:
l = [1,2,3,4,5] print(l.__iter__()) l_iterator = iter(l) #建議用iter(l) print(set(dir(l_iterator))-set(dir(l))) #結果: #<list_iterator object at 0x000001FDD1B79048> #{'__length_hint__', '__next__', '__setstate__'}迭代器
iterator:迭代器;迭代程式
迭代器協議:必須擁有__iter__方法和__next__方法
通過iter(x)得到的結果就是一個迭代器,
x是一個可迭代的對象
在for迴圈中,就是在內部調用了__next__方法才能取到一個一個的值。
__next__的精髓:
l = [1,2,3,4,5] l_iterator = iter(l) print(l_iterator.__next__()) print(l_iterator.__next__()) print(l_iterator.__next__()) print(l_iterator.__next__()) print(l_iterator.__next__()) next(l_iterator) #==l_iterator.__next__() while True: try: print(next(l_iterator)) except StopIteration: break__next__方法的使用精髓
如果我們一直取next取到迭代器里已經沒有元素了,就會報錯(拋出一個異常StopIteration),告訴我們,列表中已經沒有有效的元素了。這個時候,我們就要使用異常處理機制來把這個異常處理掉。try_except異常處理機制只做瞭解,不是本章重點,會面會詳細講解。
判斷是否可迭代和迭代器的簡潔方法:
from collections import Iterable from collections import Iterator s = 'abc' print(isinstance(s,Iterable)) print(isinstance(s,Iterator)) print(isinstance(iter(s),Iterator))判斷可迭代和迭代器
不管是一個迭代器還是一個可迭代對象,都可以使用for迴圈遍歷
迭代器出現的原因 幫你節省記憶體
三、生成器
迭代器大部分都是在python的內部去使用的,我們直接拿來用就行了
我們自己寫的能實現迭代器功能的東西就叫生成器。
1.生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2.生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
生成器Generator:
本質:迭代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)
特點:惰性運算,開發者自定義
#生成器函數 def func(): print('aaaa') a = 1 yield a #返回第一個值 print('bbbb') yield 12 #返回第二個值 ret = func() #拿到一個生成器 # print(ret) #<generator object func at 0x0000028AE2DA2EB8> print(next(ret)) #取第一個值 print(next(ret)) #取第二個值 print(next(ret)) #取第三個值 會報錯 因為沒有第三個值 def make_cloth(): for i in range(2000000): yield "第%s件衣服"%i szq = make_cloth() print(next(szq)) print(next(szq)) print(next(szq)) for i in range(50): print(next(szq))生成器函數
生成器的好處:不會一下子在記憶體中生成太多數據
其它應用:
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 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) print(g_avg.send(10)) print(g_avg.send(30)) print(g_avg.send(5))計算移動平均值簡單
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))計算移動平均值升級_生成器激活裝飾器
def func(): # for i in 'AB': # yield i yield from 'AB' #等同於上面兩行 yield from [1,2,3] g = func() print(next(g)) print(next(g)) print(next(g)) print(next(g))yield from
四、列表推導式和生成器表達式
for i in range(100): print(i*i) l =[i*i for i in range(100)] #列表推導式 print(l) l = [{'name':'v','age':28},{'name':'v'}] name_list = [dic['name'] for dic in l] #列表推導式 print(name_list) l = [{'name':'v1','age':28},{'name':'v2'}] name_list_generator = (dic['name'] for dic in l) #生成器表達式 print(name_list_generator) print(next(name_list_generator)) print(next(name_list_generator)) egg_list=['雞蛋%s' %i for i in range(10)] #列表推導式 print(egg_list) laomuji = ('雞蛋%s' %i for i in range(1,11)) #生成器表達式 print(laomuji) print(next(laomuji)) print(next(laomuji))列表推導式和生成器表達式
使用生成器的優點:
1、延遲計算,一次返回一個結果。也就是說,它不會一次生成所有的結果,這對於大數據量處理,將會非常有用。
2、提高代碼可讀性
#列表解析 sum([i for i in range(100000000)])#記憶體占用大,機器容易卡死 #生成器表達式 sum(i for i in range(100000000))#幾乎不占記憶體
總結:
1、把列表解析的[]換成()得到的就是生成器表達式
2、列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省記憶體
3、Python不但使用迭代器協議,讓for迴圈變得更加通用。大部分內置函數,也是使用迭代器協議訪問對象的。例如, sum函數是Python的內置函數,該函數使用迭代器協議訪問對象,而生成器實現了迭代器協議,所以,我們可以直接這樣計算一系列值的和
print(sum([1,2,3])) print(sum(range(1,4))) print(sum(x ** 2 for x in range(4))) print(sum([x ** 2 for x in range(4)]))
思維導圖:
預習答案:
def grep_file(filename,grep_content): f = open(filename) for line in f: if grep_content in line: yield line g = grep_file('tmp_file','python') for line in g: print(line,end='')普通青年版(1題)
def init(func): def inner(*args,**kwargs): g = func(*args,**kwargs) next(g) return g return inner @init #grep_file = init(grep_file) def grep_file(grep_content,printer_g): while True: filename = yield f = open(filename) for line in f: if grep_content in line: printer_g.send(line) @init ##printer = init(printer) 激活print_g def printer(): while True: line = yield if line:print(line,end='') grep_g = grep_file('python', printer()) grep_g.send('tmp_file')妖孽青年版(1題)
import os def getpath(filepath): g = os.walk(filepath) for par_dir, _, files in g: for file in files: yield par_dir + "\\" + file # 2打開文件對象發給3 def getfile(filepaths): for filepath in filepaths: with open(filepath, encoding="utf-8") as file: yield file # 3讀取每一行發給4 def getline(files): for file in files: for line in file: yield line # 4判斷pattern發給5 def grep(lines, pattern): for line in lines: if pattern in line: yield line # 5列印文件名 def printname(strings): for string in strings: print(string) filepath = r"F:\Code\Python\LearnPython\Day14 generator" pattern = "jean" printname(grep(getline(getfile(getpath(filepath))), pattern))妖孽升級版(2題)