# -*- coding:utf-8 -*- # 迭代--------- # 如果給定一個list或tuple,我們可以通過for循環來遍歷這個list或tuple,這種遍歷稱爲迭代 # 在python中,迭代通過for...in完成,而很多語言比如c或者Java,迭代通過下標完成,如Java代碼 ''' for (i = 0,i<list.length;i++){ n = list[i] } 可以看出,python的for循環抽象成都要高於Java的for循環,因爲python的for循環不僅僅可以用在list 或者tuple上,還可作用在其他迭代器對象上,list這種數據類型雖然有下標,但很多其他數據類型是沒 有下標的,但是只要是可迭代的對象,無論有無下標,都可以迭代,比如dict就可以 ''' d = dict(a = 1,b = 2,c = 3); print d; #{'a': 1, 'c': 3, 'b': 2} for key in d: print key; # a c b dict不是按照list順序排列,所以,迭代出的結果順序可能不一樣。 #默認情況下 dict迭代的是key,如果要迭代value,可以用forvalue in d.itervalues(),如果要同時 # 迭代key和value,可以用for k,v in d.iteritems() # 字符串也是可迭代對象,因此,也可以作用於for循環 for c in 'abcd': print c; #a b c d #所以當使用for循環時,只要作用於一個可迭代對象,for循環就可以正常運行,而我們不太關心該對象是list # 還是其他數據類型 #判斷一個對象是否可迭代用collections模塊的Iterable from collections import Iterable print isinstance('xyz',Iterable); #True str print isinstance([1,2,3],Iterable); #True list print isinstance(123,Iterable); # False 整數 print isinstance((1,2,3),Iterable); #True 元組 # 如果要對list實現類似Java那樣下標循環,python內置的enumerate函數可以吧一個list變成索引元素對 # 這樣就可以for循環中同時迭代索引和元素本身 for i,value in enumerate (['a','b']): print i,value; ''' 0 a 1 b ''' # 上面for循環裏,同時引用了兩個變量,在python中很常見,如下面代碼 for x,y in [(1,1),(2,3),(4,5)]: print x,y; ''' 1 1 2 3 4 5 ''' #------------------------------------------------------------------------- #--------包裝----------------- # 用functools.partial()可以將函數包裝成更簡潔的版本 from functools import partial def test(a,b,c): print a,b,c; f = partial(test,b = 2,c = 3); #爲後續參數提供默認命名值 print f(7); # 7 2 3 f = partial(test,7,c = 3) #爲前面位置參數和後面的命名參數提供默認值 print f(5); # 7 5 3 #python會按下面的規則合併參數 from functools import partial def partial(func,*d_args,**d_kwargs): def wrap(*args,**kwargs): new_args = d_args + args; #合併位置參數,partial提供的默認值優先 nwe_kwargs = d_kwargs.copy(); #合併命名參數,partial提供的會被覆蓋 new_kwargs.update(kwargs); print new_args; print nwe_kwargs; return fuc(*new_args,**new_kwargs); return wrap f = partial(1,'ddd',b = 2) wrap('aa',c = 1) #暫未輸出 #------------------------------------------------------------------- #---列表生成式 ''' 列表生成式即list comparehensions,是python內置的非常簡單卻很強大的可以用來創建list的生成式 舉例生成list[1,2,3,4,5,6,7]可以用list(xrange(1,8)) ''' print list(range(1,8)); #[1, 2, 3, 4, 5, 6, 7] #生成[1*1,2*2,...7*7] #循環 l = []; for x in xrange(1,8): l.append(x*x); print l; #[1, 4, 9, 16, 25, 36, 49] #列表生成式 print list([x * x for x in xrange(1,8)]);#[1, 4, 9, 16, 25, 36, 49] print [x * x for x in xrange(1,8)] #[1, 4, 9, 16, 25, 36, 49] # 寫列表生成式時,要把生成的元素x*x放在前面,後面跟for循環,就可以把list創建出來 # for後可以加IF判斷,這樣就可以篩選出偶數或奇數的平方 print [x * x for x in xrange(1,8) if x % 2 ==0];#[4, 16, 36] print [x * x for x in xrange(1,8) if x % 2 != 0]; #[1, 9, 25, 49] # 用兩層循環生成全排列 print [m + n for m in 'ABC' for n in '1','2','3'] # ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'] # 列出當前目錄下的所有文件和目錄名 import os #導入os模塊 print [s for s in os.listdir('.')]; ''' '['aaa.py', 'demo1.12.25.py', 'demo2.12.26.py', 'demo3.12.26.py', 'demo4.12.27.py', 'demo5.12.27.py', 'demo6.12.28.py', 'demo7.01.2.py', 'Include', 'Lib', 'Scripts', 'tcl'] ''' # for循環可以同時使用兩個甚至多個變量,比如dict的items()可以同事迭代key和value d = dict(a = 'A',b = 'B',c = 'c'); print d; #{'a': 'A', 'c': 'c', 'b': 'B'} for k,v in d.items(): print k,':',v ''' a : A c : c b : B ''' #列表生成式也可以使用兩個變量來生成list; d = dict(x = 'X',y = 'Y',z = 'Z'); print [k + '=' + v for k, v in d.items()] #['y=Y', 'x=X', 'z=Z'] #將一個list中的所有字符串變小寫 L = ['HHHe','WWo','APPLE']; print [s.lower() for s in L]; #['hhhe', 'wwo', 'apple'] #--------------------------------------------------------------------------- #---生成器-- ''' 通過列表生成式,我們可以直接創建一個表,但是受到內存限制,列表容量肯定是有限的。而且,創建一個包含 100萬個元素的列表,不僅佔用很大的存儲空間,如果我們只訪問前面的幾個元素,那後面絕大多數元素佔用的 空間都浪費了 所以,如果列表元素可以按照某種算法推算出來,那我們就可以在循環的過程中不斷推算出後續的元素,這樣就 不必創建完整的list,從而節省大量的空間,在python中,這種一邊循環,一邊計算的機制交生成器 generator 創建一個generater,有很多方法,第一種簡單的方法,只要把一個列表生成式的[]改成() ''' L = [x * x for x in xrange(1,5)]; #[1, 4, 9, 16] print L; g = (x * x for x in xrange(1,5)); print g ; #<generator object <genexpr> at 0x000000000499D438> ''' 創建L和g的區別僅在於自外層的[]和(),L是list,g是generator,我們可以直接打印出list的每一個元素, 而g是一個generator。可以直接打印出list的每一個元素,用next()函數獲得generator的下一個返回值, ''' print next(g); #1 print next(g); #4 print next(g); #9 #可以用for循環,generator可迭代 g = (x * x for x in xrange(1,5)); for n in g: print (n); # 1,4,9,16 ''' 所以當創建了一個generator後,基本上不會用next()而是用for來迭代他,並且不需要關心StopIteration 錯誤。 generator非常強大,如果推算的算法比較複雜,用類似的列表生成的for循環無法實現的時候,還可以用函數實現 如,著名的斐波拉契數列fibonacci,除第一個和第二個數外,任意一個數都可以由前兩個數相加得到1,1,2,3,5,8... 用列表生成式寫不出來,但是,用函數很容易 ''' def fib(x): n,a,b = 0,0,1; while n<x: print (b); a,b = b,a+b; n = n+1; return 'done'; fib(5); # 1,1,2,3,5 ''' a,b = b,a + b 相當於 t = (b,a + b); #t是一個tuple a = t[0]; b = t[1]; 但不必顯式寫出臨時變量t就可以賦值 上面的函數可以輸出斐波納挈數列的前n個數 仔細觀察,fib函數實際上是定義了斐波那契數列的推算規則,可以從第一個元素開始,推算後續任意的元素,這種 邏輯很類似generator 也就是說,上面的函數和generator僅一部之遙,要把fib函數變成generator,只把print(b)改爲yield(b) ''' def fib(x): n, a, b = 0, 0, 1 while n < x: yield b a, b = b, a + b n = n + 1 print fib(6) #<generator object fib at 0x000000000503A5A0> # 這是定義generator的另一個方法,如果一個函數定義中包含yield關鍵字,那麼這個函數就不是普通函數, # 而是generator ''' 這裏難理解的就是generator和函數的執行流程不一樣,函數是順序執行,遇到return語句或者最後一行函數 就返回,而變成generator函數,在每次調用next的時候執行,遇到yield語句返回,再次執行是從上次返回的 yield處繼續執行. ''' # 舉一個簡單的例子,定義一個generator,依次返回數字1.3.5 def odd(): print('step 1'); yield 1; print('step 2'); yield 3; print ('step 3'); yield 5; o = odd() print next(o); ''' step 1 1 ''' print next(o); ''' step 2 3 ''' print next(o); ''' step 3 5 可以看到,odd不是普通函數,而是generator,在執行過程中,遇到yield就中斷,執行3次後沒有yield可執行 了就會報錯 回到fib例子,在循環過程中不斷調用yield,就會不斷中斷,當然要給循環設置一個條件來退出循環,不然就會產生 無線數列 同樣的,把函數改成generator後,基本上不會用next來獲取下一個返回值,而是用for迭代 ''' for n in fib(5): print (n); # 1 1 2 3 5 ''' 但是用for循環調用generator時,發現拿不到generator的return語句的返回值,必須捕獲stopIteration 錯誤,返回值包含在stopIteration的value中 ''' g = fib(5); while True: try: x = next(g); print('g:'),x; except StopIteration as e: print('Generator return value:',e); break; ''' g: 1 g: 1 g: 2 g: 3 g: 5 ('Generator return value:', StopIteration()) ''' #---------------------------------------------------------- #----迭代器 ''' 可以直接作用於for循環的數據類型有,一類爲集合數據類型(list tuple dict str.. 一類是generator,包括生成器和帶yield的generator function 這些能直接作用於for循環的對象統稱爲可迭代對象Iterable,可以用isinstance()判斷是否可迭代 生成器不但可以作用於for循環,還可以被next()函數不斷調用並返回下一個值,知道最後拋出StopIteration 可以被next調用並不斷返回下一個值的對象叫迭代器:Iterator 生成器都是Iterator 對象,但list dict str 雖然是Iterable,卻不是Iterator 用isinstance()判斷一個對象是否是Iterator對象 把list dict str 等iterable(可迭代對象)變成Iterator(迭代器)用ITER()函數 ''' from collections import Iterator; print isinstance((x for x in range(5)),Iterator); #True print isinstance([],Iterator); #False print isinstance({},Iterator); #False print isinstance('abcd',Iterator); #False print isinstance(iter([]),Iterator); #True print isinstance(iter('azx'),Iterator); #True ''' 爲什麼list dict str等數據類型不是Iterator 因爲python的Iterator對象表示一個數據流,Iterator對象可以被next函數調用並不斷返回下一個數據,直到 沒數據拋出StopIterateration錯誤,可以把這個數據流看成是一個有序的序列,但我們卻不知道序列的長度,只能 通過不斷調用next函數實現按需計算下一個數據,所以Iterator得計算是有惰性得,只有在需要返回下一個數據 時他纔會計算 Iterator甚至可表示一個無線大得數據流,例如全體自然數,然而使用list是永遠不可能存儲全體自然數的 凡是可用於for循環的對象都是Iterator類型 凡是可作用於next函數的對象都是Iterator類型,他們表示一個惰性計算的序列 集合數據類型list dict str等是Iterable但不是Iterator,可通過iter()函數獲得一個Iterator對象 python的for循環本質就是不斷調用next函數實現 ''' #--------------------------------------------------------------------------- #--補充 # ----------------------------------------------------------------------- #-----迭代器 # 迭代器協議,僅需要__iter__()和next()兩個方法,前者返回迭代器對象,後者依次返回數值,直到引發 # stopIteration異常結束 # 最簡單的做法是用內置函數iter(),它返回常用類型的迭代器包裝對象,問題是,序列類型已經可以被for處理 # 爲什麼還要這麼做? class Data(object): def __init__(self): self._data = [] def add(self, x): self._data.append(x) def data(self): return iter(self._data) d = Data(); d.add(1) d.add(2) d.add(3) for x in d.data(): print x; #1 2 3 # 返回迭代器對象self._data列表,可避免對象狀態被外部修改,或許你會嘗試返回tuple,但這需要複製整個 # 列表,浪費更多的內存 ''' iter()很方便,但無法讓迭代中途停止,這需自己動手實現迭代器對象。在設計原則上,通常會將迭代器從數據 對象中分離出去,因爲迭代器需要維持狀態,而且可能有多個迭代器在同時操控數據,這些不該成爲數據對象的 負擔,無端提升了複雜度 ''' class Data(object): def __init__(self,*args): self._data = list(args); def __iter__(self): return DataIter(self); class DataIter(object): def __init__(self,data): self._index = 0; self._data = data._data; def next(self): if self._index >= len(self._data): raise StopIteration(); d = self._data[self._index]; self._index += 1; return d; d = Data(1,2); for x in d: print x; # 1 2 #Data 僅僅是數據容器,只需__iter__返回迭代器對象,而由DataIter生成器提供next方法 # 除了for循環,迭代器也可以直接用next()操控 s = Data ('a','b','c'); it = iter(s); print it #<__main__.DataIter object at 0x00000000057FF780> print next(it); #a print next(it); #b print next(it); #c # print next(it); # raise StopIteration(); print '不用next,用for' for i in s : print i; #a b c print '---------------------------------------------------------------------' #--生成器 # 基於索引實現的迭代器有些醜陋,更合理的做法是用yield返回實現了迭代器協議Generator對象 class Data(object): def __init__(self,*args): self._data = list(args); def __iter__(self): for x in self._data: yield x; d = Data(1,2,3); for x in d: print x; # 1,2,3 # 編譯器會將包含yield的方法或函數重新打包,使其返回Generator對象,這樣一來,就無需費力氣維護額外的 # 迭代器類型了 print '這是用next' print d.__iter__()#<generator object __iter__ at 0x0000000004BACC60> print iter(d).next(); #1 print iter(d).next(); #1 ???bug 不懂 print iter(d).next(); #1
迭代,迭代器,生成器
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.