什麼是疊代 (iterable)
字元串、清單、元組、字典、集合都可以被for循環,說明他們都是可疊代的。
可以直接作用于for循環的對象統稱為可疊代對象(Iterable)。
可以被next()函數調用并不斷傳回下一個值的對象稱為疊代器(Iterator)。
所有的Iterable均可以通過内置函數iter()來轉變為Iterator。
對疊代器來講,有一個__next__()就夠了。在你使用for 和 in 語句時,程式就會自動調用即将被處理的對象的疊代器對象,然後使用它的__next__()方法,直到監測到一個StopIteration異常。
可疊代協定
我們現在是從結果分析原因,能被for循環的就是“可疊代的”,但是如果正着想,for怎麼知道誰是可疊代的呢?
假如我們自己寫了一個資料類型,希望這個資料類型裡的東西也可以使用for被一個一個的取出來,那我們就必須滿足for的要求。這個要求就叫做“協定”。
可以被疊代要滿足的要求就叫做可疊代協定。可疊代協定的定義非常簡單,就是内部實作了__iter__方法。
1 >>> L = [1,2,3]
2 >>> [x**2 for x in L]
3 [1, 4, 9]
4 >>> next(L)
5 Traceback (most recent call last):
6 File "<stdin>", line 1, in <module>
7 TypeError: 'list' object is not an iterator
8 >>> I=iter(L)
9 >>> next(I)
10 1
11 >>> next(I)
12 2
13 >>> next(I)
14 3
15 >>> next(I)
16 Traceback (most recent call last):
17 File "<stdin>", line 1, in <module>
18 StopIteration
View Code
上面例子中,清單L可以被for進行循環但是不能被内置函數next()用來查找下一個值,是以L是Iterable。
L通過iter進行包裝後設為I,I可以被next()用來查找下一個值,是以I是Iterator。
- 内置函數iter()僅僅是調用了對象的__iter()方法,是以list對象内部一定存在方法iter__()
- 内置函數next()僅僅是調用了對象的__next()方法,是以list對象内部一定不存在方法next__(),但是Itrator中一定存在這個方法。
- for循環内部事實上就是先調用iter()把Iterable變成Iterator在進行循環疊代的
1 >>> L = [4,5,6]
2 >>> I = L.__iter__()
3 >>> L.__next__()
4 Traceback (most recent call last):
5 File "<stdin>", line 1, in <module>
6 AttributeError: 'list' object has no attribute '__next__'
7 >>> I.__next__()
8 4
9 >>> from collections import Iterator, Iterable
10 >>> isinstance(L, Iterable)
11 True
12 >>> isinstance(L, Iterator)
13 False
14 >>> isinstance(I, Iterable)
15 True
16 >>> isinstance(I, Iterator)
17 True
18 >>> [x**2 for x in I]
19 [25, 36]
View Code
疊代器
疊代器遵循疊代器協定:必須擁有__iter__方法和__next__方法。
具有通路生成器的能力,可以通路到生成器的值,類似于生成器的
__next__
方法,一個一個值一個值得去疊代,隻能夠按照順序的去查找。
特點:
- 通路者不需要關心疊代器内部的結構,僅需通過next()方法不斷去取下一個内容
- 不能随機通路集合中的某個值 ,隻能從頭到尾依次通路
- 通路到一半時不能往回退
- 便于循環比較大的資料集合,節省記憶體
1 print('__next__' in dir(range(12))) #檢視'__next__'是不是在range()方法執行之後内部是否有__next__
2 print('__iter__' in dir(range(12))) #檢視'__next__'是不是在range()方法執行之後内部是否有__next__
3
4 from collections import Iterator
5 print(isinstance(range(100000000),Iterator)) #驗證range執行之後得到的結果不是一個疊代器
View Code
yield from
1 def gen1():
2 for c in 'AB':
3 yield c
4 for i in range(3):
5 yield i
6
7 print(list(gen1()))
8
9 def gen2():
10 yield from 'AB'
11 yield from range(3)
12
13 print(list(gen2()))
View Code
生成器
1.生成器函數:正常函數定義,但是,使用yield語句而不是return語句傳回結果。yield語句一次傳回一個結果,在每個結果中間,挂起函數的狀态,以便下次重它離開的地方繼續執行
2.生成器表達式:類似于清單推導,但是,生成器傳回按需産生結果的一個對象,而不是一次建構一個結果清單
生成器Generator:
本質:疊代器(是以自帶了__iter__方法和__next__方法,不需要我們去實作)
特點:惰性運算,開發者自定義
生成器函數
一個包含yield關鍵字的函數就是一個生成器函數。yield可以為我們從函數中傳回值,但是yield又不同于return,return的執行意味着程式的結束,調用生成器函數不會得到傳回的具體的值,而是得到一個可疊代的對象。每一次擷取這個可疊代對象的值,就能推動函數的執行,擷取新的傳回值。直到函數執行結束。
僅僅擁有生成某種東西的能力,如果不用
__next__
方法是擷取不到值得。
建立一個生成器函數
1 >>> def scq():
2 print("11")
3 # 當函數代碼塊中遇到yield關鍵字的時候,這個函數就是一個生成器函數
4 yield 1
5 print("22")
6 yield 2
7 print("33")
8 yield 3
9
10
11 # 把生成器指派給一個對象
12
13 >>> r = scq()
14
15 # 檢視r的蘇劇類型并且輸出r的值
16
17 >>> print(type(r),r)
18 <class 'generator'> <generator object scq at 0x000001F117D8DF10>
19
20 # 當執行生成器的__next__的時候,代碼會按照順序去執行,當執行到yield時會傳回并
21 # 提出,yield後面的值就是傳回值,然後記錄代碼執行的位置,并退出
22
23 >>> ret = r.__next__()
24 11
25
26 # 第二次執行的時候會根據上次代碼執行的位置繼續往下執行
27
28 >>> ret = r.__next__()
29 22
30 >>> ret = r.__next__()
31 33
32
33 # 如果__next__擷取不到值的時候就會報StopIteration錯誤
34
35 >>> ret = r.__next__()
36 Traceback (most recent call last):
37 File "<stdin>", line 1, in <module>
38 StopIteration
View Code
利用生成器建立一個range
1 # 建立一個生成器函數,函數名是range,n是傳入的參數,也是輸出的數的最大值
2 def range(n):
3 # 預設從0開始
4 start = 0
5 # 進入while循環,如果最小值小于最大值就進入循環
6 while start < n:
7 # 第一次傳回start,下面代碼不執行
8 yield start
9 # 第二次進來的時候start = start + 1,然後進入下一次循環
10 start += 1
11
12 # 停止的參數為5
13 obj = range(5)
14 # 第一個數指派給n1
15 n1 = obj.__next__()
16 # 第二個數指派給n2
17 n2 = obj.__next__()
18 # 第三個數指派給n3
19 n3 = obj.__next__()
20 # 第四個數指派給n4
21 n4 = obj.__next__()
22 # 第五個數指派給n5
23 n5 = obj.__next__()
24
25 # 輸出這五個數的值
26 print(n1,n2,n3,n4,n5)
27
28 # 執行結果
29
30 C:\Python35\python.exe F:/Python_code/sublime/Week5/Day03/s1.py
31 0 1 2 3 4
32
33 Process finished with exit code 0
View Code
生成器監聽檔案輸入的例題
1 import time
2
3
4 def tail(filename):
5 f = open(filename)
6 f.seek(0, 2) #從檔案末尾算起
7 while True:
8 line = f.readline() # 讀取檔案中新的文本行
9 if not line:
10 time.sleep(0.1)
11 continue
12 yield line
13
14 tail_g = tail('tmp')
15 for line in tail_g:
16 print(line)
View Code
計算移動平均值(1)
1 def averager():
2 total = 0.0
3 count = 0
4 average = None
5 while True:
6 term = yield average
7 total += term
8 count += 1
9 average = total/count
10
11
12 g_avg = averager()
13 next(g_avg)
14 print(g_avg.send(10))
15 print(g_avg.send(30))
16 print(g_avg.send(5))
View Code
計算移動平均值(2)預激協程的裝飾器
1 def init(func): #在調用被裝飾生成器函數的時候首先用next激活生成器
2 def inner(*args,**kwargs):
3 g = func(*args,**kwargs)
4 next(g)
5 return g
6 return inner
7
8 @init
9 def averager():
10 total = 0.0
11 count = 0
12 average = None
13 while True:
14 term = yield average
15 total += term
16 count += 1
17 average = total/count
18
19
20 g_avg = averager()
21 # next(g_avg) 在裝飾器中執行了next方法
22 print(g_avg.send(10))
23 print(g_avg.send(30))
24 print(g_avg.send(5))
View Code
清單推導式和生成器表達式
1 #老男孩由于峰哥的強勢加盟很快走上了上市之路,alex思來想去決定下幾個雞蛋來報答峰哥
2
3 egg_list=['雞蛋%s' %i for i in range(10)] #清單解析
4
5 #峰哥瞅着alex下的一筐雞蛋,捂住了鼻子,說了句:哥,你還是給我隻母雞吧,我自己回家下
6
7 laomuji=('雞蛋%s' %i for i in range(10))#生成器表達式
8 print(laomuji)
9 print(next(laomuji)) #next本質就是調用__next__
10 print(laomuji.__next__())
11 print(next(laomuji))
View Code
各種推導式詳解
推導式的套路
之前我們已經學習了最簡單的清單推導式和生成器表達式。但是除此之外,其實還有字典推導式、集合推導式等等。
下面是一個以清單推導式為例的推導式詳細格式,同樣适用于其他推導式。
1 variable = [out_exp_res for out_exp in input_list if out_exp == 2]
2 out_exp_res: 清單生成元素表達式,可以是有傳回值的函數。
3 for out_exp in input_list: 疊代input_list将out_exp傳入out_exp_res表達式中。
4 if out_exp == 2: 根據條件過濾哪些值可以。
View Code
清單推導式
例一:30以内所有能被3整除的數
1 multiples = [i for i in range(30) if i % 3 is 0]
2 print(multiples)
3 # Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
View Code
例二:30以内所有能被3整除的數的平方
1 def squared(x):
2 return x*x
3 multiples = [squared(i) for i in range(30) if i % 3 is 0]
4 print(multiples)
View Code
例三:找到嵌套清單中名字含有兩個‘e’的所有名字
1 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
2 ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
3
4 print([name for lst in names for name in lst if name.count('e') >= 2]) # 注意周遊順序,這是實作的關鍵
View Code
字典推導式
例一:将一個字典的key和value對調
1 mcase = {'a': 10, 'b': 34}
2 mcase_frequency = {mcase[k]: k for k in mcase}
3 print(mcase_frequency)
View Code
例二:合并大小寫對應的value值,将k統一成小寫
1 mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
2 mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
3 print(mcase_frequency)
View Code
集合推導式
例:計算清單中每個值的平方,自帶去重功能
1 squared = {x**2 for x in [1, -1, 2]}
2 print(squared)
3 # Output: set([1, 4])
View Code
練習題:
例1: 過濾掉長度小于3的字元串清單,并将剩下的轉換成大寫字母
例2: 求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇數組成的元祖清單
例3: 求M中3,6,9組成的清單M = [[1,2,3],[4,5,6],[7,8,9]]
1 1.[name.upper() for name in names if len(name)>3]
2 2.[(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1]
3 3. [row[2] for row in M]
View Code
總結:
1.把清單解析的[]換成()得到的就是生成器表達式
2.清單解析與生成器表達式都是一種便利的程式設計方式,隻不過生成器表達式更節省記憶體
3.Python不但使用疊代器協定,讓for循環變得更加通用。大部分内置函數,也是使用疊代器協定通路對象的。例如, sum函數是Python的内置函數,該函數使用疊代器協定通路對象,而生成器實作了疊代器協定,是以,我們可以直接這樣計算一系列值的和:
1 sum(x ** 2 for x in range(4))
2
3 # 而不用多此一舉的先構造一個清單:
4
5 sum([x ** 2 for x in range(4)])
View Code
可疊代對象:
擁有__iter__方法
特點:惰性運算
例如:range(),str,list,tuple,dict,set
疊代器Iterator:
擁有__iter__方法和__next__方法
例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o
生成器Generator:
本質:疊代器,是以擁有__iter__方法和__next__方法
特點:惰性運算,開發者自定義
使用生成器的優點:
1.延遲計算,一次傳回一個結果。也就是說,它不會一次生成所有的結果,這對于大資料量處理,将會非常有用。
2.提高代碼可讀性
1 #清單解析
2 sum([i for i in range(100000000)])#記憶體占用大,機器容易卡死
3
4 #生成器表達式
5 sum(i for i in range(100000000))#幾乎不占記憶體
View Code
生成器相關的面試題
生成器在程式設計中發生了很多的作用,善用生成器可以幫助我們解決很多複雜的問題
除此之外,生成器也是面試題中的重點,在完成一些功能之外,人們也想出了很多魔性的面試題。
面試題(1)
1 def demo():
2 for i in range(4):
3 yield i
4
5 g=demo()
6
7 g1=(i for i in g)
8 g2=(i for i in g1)
9
10 print(list(g1))
11 print(list(g2))
View Code
面試題(2)
1 def add(n,i):
2 return n+i
3
4 def test():
5 for i in range(4):
6 yield i
7
8 g=test()
9 for n in [1,10]:
10 g=(add(n,i) for i in g)
11
12 print(list(g))
View Code
tail&grep
1 def demo():
2 for i in range(4):
3 yield i
4
5 g=demo()
6
7 g1=(i for i in g)
8 g2=(i for i in g1)
9
10 print(list(g1))
11 print(list(g2))
12
13 複制代碼
14 複制代碼
15
16 def add(n,i):
17 return n+i
18
19 def test():
20 for i in range(4):
21 yield i
22
23 g=test()
24 for n in [1,10]:
25 g=(add(n,i) for i in g)
26
27 print(list(g))
28
29 複制代碼
30 複制代碼
31
32 import os
33
34 def init(func):
35 def wrapper(*args,**kwargs):
36 g=func(*args,**kwargs)
37 next(g)
38 return g
39 return wrapper
40
41 @init
42 def list_files(target):
43 while 1:
44 dir_to_search=yield
45 for top_dir,dir,files in os.walk(dir_to_search):
46 for file in files:
47 target.send(os.path.join(top_dir,file))
48 @init
49 def opener(target):
50 while 1:
51 file=yield
52 fn=open(file)
53 target.send((file,fn))
54 @init
55 def cat(target):
56 while 1:
57 file,fn=yield
58 for line in fn:
59 target.send((file,line))
60
61 @init
62 def grep(pattern,target):
63 while 1:
64 file,line=yield
65 if pattern in line:
66 target.send(file)
67 @init
68 def printer():
69 while 1:
70 file=yield
71 if file:
72 print(file)
73
74 g=list_files(opener(cat(grep('python',printer()))))
75
76 g.send('/test1')
77
78 協程應用:grep -rl /dir
View Code
轉載于:https://www.cnblogs.com/panfb/p/7811270.html