python (八)迭代器、生成器、列表推导式
一、迭代器
1、先來講講什么是可迭代對象
字符串、列表、元組、字典、集合都可以被for循環,說明他們都是可迭代的。
?
2、怎么判斷是不是一個可迭代對象
判定方法:內部含有‘__iter__’方法的數據就是可迭代對象
可迭代對象的種類:list str tuple set dict range() 文件句柄
s1 = '我叫王大錘' print(dir(s1)) # 查看對象S1中變量、方法 print('__iter__' in dir(s1)) # 判斷__iter__方法是否在對象S1的所有方法中,存在返回true,不存在返回false?
3、怎么判斷這個對象是不是迭代器
判定方法:內部含有__iter__方法的并且含有__next__方法的就是迭代器
# 判斷是否是迭代器 f1 = open('a.txt', encoding='utf-8') print('__iter__' in dir(f1)) # 判斷是否有__iter__方法 print('__next__' in dir(f1)) # 判斷是否有__next__方法 f1.close()? ?迭代器就是在迭代對象的基礎上多了個__next__方法。既然這樣他們肯定是可以互相轉換的
? 轉換方法:
? ?可迭代對象 ----> 迭代器 :可迭代對象 .?__next__()
# 循環輸出每個元素 # 第一種方法 l1 = [1, 2, 3, '大錘'] # 這是個迭代對象 iter1 = iter(l1) # 這一步也可以用 l1.__iter__() 表示 print(iter1) # 輸出iter1的內存地址 print(iter1.__next__()) # 1 print(iter1.__next__()) # 2 print(iter1.__next__()) # 3 print(iter1.__next__()) # 大錘 print(iter1.__next__()) # 超出元素個數,會報錯# 第二種方法 l1 = [1, 2, 3, '大錘'] for i in l1: # 循環輸出每個元素print(i)# 第三種方法l1 = [1, 2, 3, '大錘'] for i in l1.__iter__(): # 循環輸出每個元素print(i)
?
4、迭代器的優點
非常非常節省內存:在循環時,同一時刻在內存中只出現一條數據,極大限度的節省了內存
他滿足惰性機制:不訪問__next__() 就沒有值
一次性取值,只能按順序取
?
5、為什么要有for循環
為什么要有for循環?
# 利用下表方式循環輸出 l = [1, 2, 3, '大錘'] index = 0 while index < len(l):print(l[index])index += 1# 上述方法用下表就可以循環輸出,為什么要用迭代器呢??
原因如下:
序列類型字符串:列表,元組都有下標,用上述的方式訪問是可以的。
但是非序列類型:字典,集合,文件對象就無法實現了。
for循環就是基于迭代器協議提供了一個統一的可以遍歷所有對象的方法,即在遍歷之前,先調用對象的__iter__方法將其轉換成一個迭代器,然后使用迭代器協議去實現循環訪問,這樣所有的對象就都可以通過for循環來遍歷了
最重要的一點,轉化成迭代器,在循環時,同一時刻在內存中只出現一條數據,極大限度的節省了內存~
for循環的優點:
?將可迭代對象轉化成迭代器;(可迭代對象.__iter__())
? 利用__next__方法一個一個取值;
? 利用異常處理終止循環。(不終止,取不到值就會報錯)
用while循環模擬for循環:?
l1 = [1, 2, 3, '大錘'] iter1 = l1.__iter__() while 1:try:print(iter1.__next__())except StopIteration: # 利用異常處理終止循環break?
二、生成器
1、什么是生成器?
在某些情況下,我們也需要節省內存,就只能自己寫。我們自己寫的這個能實現迭代器功能的東西就叫生成器。
? ?生成器:自己用python代碼寫的迭代器 本質就是迭代器
?
2、Python中提供的生成器
? 生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次從它離開的地方繼續執行
? 生成器表達式:類似于列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
?
3、生成器函數(函數式寫法的生成器)
判定方法:
? ? 只要函數中出現yield,那么 他就不是函數了,他是生成器函數。
? yield可以為我們從函數中返回值,但是yield又不同于return,return的執行意味著程序的結束,調用生成器函數不會得到返回的具體的值,而是得到一個可迭代的對象。每一次獲取這個可迭代對象的值,就能推動函數的執行,獲取新的返回值。直到函數執行結束。
?
# 普通函數 def func1(): # 函數中出現了yield,此函數為生成器函數print(111)print(222)yield 3yield 4yield 5yield 6yield 7 g = func1() # 生成器對象 print(g) # <generator object func1 at 0x10748fa98> 輸出的是這個函數的內存地址 print(g.__next__()) # 等同于print(next(g)),輸出結果為111 222 3 # 一個next 對應一個 yield print(g.__next__()) # 4 print(g.__next__()) # 5 print(g.__next__()) # 6?
# 加了模塊的函數 import time # 使用了模塊的函數加了yield也是生成器函數 def func():a = 1print('現在定義了a變量')yield ab = 2print('現在又定義了b變量')yield bg1 = func() print('g1 : ', g1) # 打印g1可以發現g1就是一個生成器 print('-'*20) # 我是華麗的分割線 print(next(g1)) # 結果:現在定義了a變量, 收到yield返回的值1 time.sleep(1) # sleep一秒看清執行過程 ,可以看出函數未被yield打斷,可以繼續執行 print(next(g1)) # 結果:現在定義了變量, 收到yield返回的值2?
小總結:
return? ?結束函數 給函數返回值
yield? ? 不會結束生成器函數,next 對應一個yield進行取值
?
4、生成器的好處
# 直接for 循環,一次性循環取出所有的值 def cloth():for i in range(1, 201):print('當前被取出來的數是 %s' % (i)) cloth()# yield 返回,每次返回一個值 def cloth1():for i in range(1, 201):yield '當前被取出來的數是 %s' % (i)g = cloth1()for i in range(5):print(g.__next__()) # 只輸出1到5 五個數for i in range(195):print(g.__next__()) # 只輸出1到195 這幾個數由上面的例子可以看出來,yield可以根據需要取值,能夠控制取值的個數,而且不會終止函數
?
5、send(與__next__類似)
# send 和 __next__的用法和對比 def func(): print(123) # 第三步 content = yield 1 # 第四步將1返回給第二步,即g._# 第六步執行完,yield 1 接print('=======', content) # 第七步 輸出 =print(456) # 第八步 輸出456 yield 2 #第九步 將值2返回給 g.__next__() g = func() # 第一步 ret = g.__next__() # 第二步 將這個函數轉換成迭代器,這是# 第四步執行完,ret接收到的值為 1 # 第九步執行完,ret接收到的值為 2 print('***', ret) # 第五步 輸出 *** 1 ret = g.send('hello') # 第六步 給第四步的yield 1 print('***', ret) # 第十步 輸出 *** 2小總結:
send 獲取下一個值的效果和next基本一致只是在獲取下一個值的時候,給上一yield的位置(例子中的第六步給第四步傳了一個值)
使用send的注意事項:
第一次使用生成器的時候 是用next獲取
最后一個yield不能接受外部的值
?
?三、列表推導式
1、列表推導式的模式
循環模式:? [變量(加工后的變量) for 變量 in iterable]
篩選模式:? [變量(加工后的變量) for 變量 in iterable if 條件]
?
# 篩選模式 # 第一種 l1 = [i for i in range(10) if i % 2 == 0] print(l1) # [0, 2, 4, 6, 8] # 第二種 # 2.[10以內所有數的平方: 1,4,9,16,.....100] print([i*i for i in range(1, 11)]) # 2.[30以內能被3整除的數的平方:39,36,81....900] print([i**2 for i in range(1, 31) if i % 3 == 0]) # 3.過濾掉長度小于3的字符串列表,并將剩下的轉換成大寫字母 l1 = ['alex', 'taibai', 'wusir', 're', 'ab'] print([i.upper() for i in l1 if len(i) > 3]) # 4.找到嵌套列表中名字含有兩個‘e’的所有名字 # 用列表推導式實現 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] print([name for lst in names for name in lst if name.count('e') >= 2]) # ['Jefferson', 'Wesley', 'Steven', 'Jennifer'] # 用之前的老方法實現 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] l1 = [] for i in names: for i1 in i: if i1.count('e') >= 2: l1.append(i1) print(l1) # ['Jefferson', 'Wesley', 'Steven', 'Jennifer']?
2、字典推導式 ?
# 將一個字典的key和value對調 dic = {'a': 10, 'b': 34} dic2 = {dic[k]: k for k in dic} print(dic2) # {10: 'a', 34: 'b'}# 合并大小寫對應的value值,將k統一成小寫 dic = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} dic2 = {k.lower(): dic.get(k.lower(), 0) + dic.get(k.upper(), 0) for k in dic.keys()} print(dic2) # {'a': 17, 'b': 34, 'z': 3}?
? 3、?集合推導式
# 計算列表中每個值的平方,自帶去重功能 squared = {x**2 for x in [1, -1, 2]} print(squared) # {1, 4}?
四、生成器表達式
g1 = (i*i for i in range(1, 3)) # 生成器表達式用() 括起來,列表推導式是用[],其他規則與列表推導式一致。 print(g1.__next__()) # 1 print(g1.__next__()) # 4 print(g1.__next__()) # 不會輸出值,會報錯,__next__方法只取1和2的值,不會取3的值?
列表推導式,生成器表達式:
優點: 構造簡單,一行完成
缺點: 不能排錯,使用debug模式時,無法看到執行步驟
他不能構建復雜的數據結構。
?
?
轉載于:https://www.cnblogs.com/caoyinshan/p/10116332.html
總結
以上是生活随笔為你收集整理的python (八)迭代器、生成器、列表推导式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring bean的作用域和生命周期
- 下一篇: 库存物资管理