迭代器
print(dir([])) # 輸出列表所有的方法
什麼叫迭代
字符串、列表、元組、字典、集合都可以被for循環,說明他們都是可迭代的。
from collections.abc 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循環的就是“可迭代的”,要想可迭代,內部必須有一個__iter__
方法。
print([1,2].__iter__())
# 結果 迭代器
<list_iterator object at 0x1024784a8>
迭代器協議
# 列表的迭代器比起列表來說實現了哪些新方法
'''
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__())
在for循環中,就是在內部調用了__next__
方法才能取到一個一個的值。
l = [1,2,3,4]
l_iter = l.__iter__()
while True:
try:
item = l_iter.__next__()
print(item)
except StopIteration:
break
迭代器遵循迭代器協議:必須擁有__iter__
方法和__next__
方法。
from collections.abc import Iterable
from collections.abc import Iterator
print(isinstance([],Itrator))
print(isinstance([],Itrable))
總結:
- 迭代器協議和可迭代協議
- 迭代器遵循迭代器協議:必須擁有
__iter__
方法和__next__
方法。 - 可迭代,內部必須有一個
__iter__
方法。 - 只要是迭代器 一定可迭代
- 可以被for循環的都是可迭代的
- 可迭代的對象
.__iter__()
方法就可以得到一個迭代器 - 迭代器中的
__next__()
方法可以一個一個的獲取值
生成器
我們自己寫的能實現迭代器功能的東西就叫生成器。
Python中提供的生成器:
- **生成器函數:**常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次從它離開的地方繼續執行
- **生成器表達式:**類似於列表推導式,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
生成器Generator:
本質:迭代器(所以自帶了__iter__
方法和__next__
方法,不需要我們去實現)
特點:惰性運算,開發者自定義
生成器函數
- 只要含有yield關鍵字的函數都是生成器函數
- yield不能和return共用且需要寫在函數內
- 調用函數之後函數不執行,返回一個生成器
- 每次調用
__next__
方法的時候會取到一個值,直到取完最後一個,再執行__next__
就會報錯 - 生成器函數:執行之後會得到一個生成器作爲返回值
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(5) #sleep一秒看清執行過程
print(next(g1))
# 運行結果
>>> g1 : <generator object genrator_fun1 at 0x000002160AEFC4F8>
>>> --------------------
>>> 現在定義了a變量
>>> 1
# 等了5秒
>>> 現在又定義了b變量
>>> 2
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)
send
def generator():
print(123)
content = yield 1
print('=======',content)
print(456)
yield 2
g = generator()
ret = g.__next__()
print('***',ret)
ret = g.send('hello') #send的效果和next一樣
print('***',ret)
#send 獲取下一個值的效果和next基本一致
#只是在獲取下一個值的時候,給上一yield的位置傳遞一個數據
#使用send的注意事項
# 第一次使用生成器的時候 是用next獲取下一個值
# 最後一個yield不能接受外部的值
獲取移動平均值
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) # g = averager()
next(g)
return g
return inner
@init # averager = init(averager) ===> inner
def averager():
total = 0.0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total/count
g_avg = averager() # ===> inner()
# 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()))
>>> ['A', 'B', 0, 1, 2]
def gen2():
yield from 'AB'
yield from range(3)
print(list(gen2()))
>>> ['A', 'B', 0, 1, 2]
列表推導式和生成器表達式
egg_list=['雞蛋%s' %i for i in range(10)] #列表解析 列表推導式
#峯哥瞅着alex下的一筐雞蛋,捂住了鼻子,說了句:哥,你還是給我只母雞吧,我自己回家下
laomuji=('雞蛋%s' %i for i in range(10))#生成器表達式
print(laomuji)
print(next(laomuji)) #next本質就是調用__next__
print(laomuji.__next__())
print(next(laomuji))
# 執行結果
>>> <generator object <genexpr> at 0x000002BCD1DDC4F8>
>>> 雞蛋0
>>> 雞蛋1
>>> 雞蛋2
總結:
- 把列表推導式的[]換成()得到的就是生成器表達式
- 列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
- Python使用迭代器協議,讓for循環變得更加通用。
- 大部分內置函數,也是使用迭代器協議訪問對象的。
# sum函數是Python的內置函數,該函數使用迭代器協議訪問對象,
# 而生成器實現了迭代器協議,所以,我們可以直接這樣計算一系列值的和:
sum(x ** 2 for x in range(4))
# 而不用多此一舉的先構造一個列表:
sum([x ** 2 for x in range(4)])
各種推導式
推導式的套路
variable = [out_exp_res for out_exp in input_list if out_exp == 2]
out_exp_res: # 列表生成元素表達式,可以是有返回值的函數。
for out_exp in input_list: # 迭代input_list將out_exp傳入out_exp_res表達式中。
if out_exp == 2: # 根據條件過濾哪些值可以。
列表推導式
例一:30以內所有能被3整除的數
multiples = [i for i in range(30) if i % 3 is 0]
print(multiples)
# Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
例二:30以內所有能被3整除的數的平方
multiples = [i*i for i in range(30) if i % 3 is 0]
print(multiples)
例三:找到嵌套列表中名字含有兩個‘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]) # 注意遍歷順序,這是實現的關鍵
字典推導式
例一:將一個字典的key和value對調
mcase = {'a': 10, 'b': 34}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)
例二:合併大小寫對應的value值,將k統一成小寫
mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
print(mcase_frequency)
集合推導式
例:計算列表中每個值的平方,自帶去重功能
squared = {x**2 for x in [1, -1, 2]}
print(squared)
# Output: set([1, 4])
練習題:
# 例1: 過濾掉長度小於3的字符串列表,並將剩下的轉換成大寫字母
[name.upper() for name in names if len(name)>3]
# 例2: 求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇數組成的元祖列表
[(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1]
# 例3: 求M中3,6,9組成的列表M = [[1,2,3],[4,5,6],[7,8,9]]
[row[2] for row in M]