據廖雪峰python3教程----python學習第十天

來源:互聯網
上載者:User

標籤:python   產生器   列表產生式   迭代器   高階函數   


列表產生式(List Comprehensions)


列表產生式即List Comprehensions,是Python內建的非常簡單卻強大的可以用來建立list的產生式。

舉個例子,要產生list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11))

>>> list(range(1,11))[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

產生[1x1,2x2,3x3,...,10x10] :

>>> L=[]>>> for x in range(1,11):     L.append(x*x)

    

>>> L[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]


另一種簡化的寫法:

>>> [x*x for x in range(1,11)][1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

寫列表產生式時,把要產生的元素 x*x 放到前面,後面跟for迴圈,就可以把list建立出來:

>>> [x*x for x in range(1,11) if x%2==0][4, 16, 36, 64, 100]
>>> [m+n for m in ‘ABC‘ for n in ‘XYZ‘][‘AX‘, ‘AY‘, ‘AZ‘, ‘BX‘, ‘BY‘, ‘BZ‘, ‘CX‘, ‘CY‘, ‘CZ‘]

列出目前的目錄下的檔案:

>>> import os>>> [d for d in os.listdir(‘.‘)][‘DLLs‘, ‘Doc‘, ‘include‘, ‘Lib‘, ‘libs‘, ‘LICENSE.txt‘, ‘NEWS.txt‘, ‘python.exe‘, ‘pythonw.exe‘, ‘README.txt‘, ‘Scripts‘, ‘students‘, ‘tcl‘, ‘Tools‘]


>>> d={‘x‘:‘A‘,‘y‘:‘B‘,‘z‘:‘C‘}>>> for k,v in d.items():     print(k,‘=‘,v)     x = Az = Cy = B
>>> [k+‘=‘+v for k,v in d.items()][‘x=A‘, ‘z=C‘, ‘y=B‘]


把一個list 中的字元竄變成小寫

>>> l=[‘Aadss‘,‘Bsad‘,‘Casd‘,‘Dasd‘]>>> [s.lower() for s in l][‘aadss‘, ‘bsad‘, ‘casd‘, ‘dasd‘]
L1 = [‘Hello‘, ‘World‘, 18, ‘Apple‘, None]# 期待輸出: [‘hello‘, ‘world‘, ‘apple‘]print(L2)>>> L2=[s.lower() for s in L1 if isinstance(s,str)]>>> L2[‘hello‘, ‘world‘, ‘18‘, ‘apple‘]>>> [isinstance(s,str) and s.lower() or s for s in l1][‘hello‘, ‘world‘, ‘18‘, ‘apple‘, None]>>> [s.lower() if isinstance(s,str) else s for s in l1][‘hello‘, ‘world‘, ‘18‘, ‘apple‘, None]

產生器

通過列表產生式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間。在Python中,這種一邊迴圈一邊計算的機制,稱為產生器:generator。


要建立一個generator,有很多種方法。第一種方法很簡單,只要把一個列表產生式的[]改成(),就建立了一個generator:

>>> L=[x*x for x in range(10)]>>> L[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> g=(x*x for x in range(10))>>> g<generator object <genexpr> at 0x02C9FC10>


建立 L 和 G 的區別僅在於最外層的 【】 和 () ,L 是一個 list ,而 G 是一個 generator。

要列印generator 的每一個元素我們需要用到next():

>>> next(g)0>>> next(g)1>>> next(g)4>>> next(g)9>>> next(g)16>>> next(g)25>>> next(g)36>>> next(g)49>>> next(g)64>>> next(g)81>>> next(g)Traceback (most recent call last):  File "<pyshell#53>", line 1, in <module>      next(g)StopIteration


當計算到最後一個元素時,跑出 StopIteration 錯誤。


當然,上面這種不斷調用next(g)實在是太變態了,正確的方法是使用for迴圈,因為generator也是可迭代對象:

>>> g=(x*x for x in range(10))>>> for n in g:     print(n)         0149162536496481



generator非常強大。如果推算的演算法比較複雜,用類似列表產生式的for迴圈無法實現的時候,還可以用函數來實現。

比如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契數列用列表產生式寫不出來,但是,用函數把它列印出來卻很容易:

>>> def fib(max):     n,a,b=0,0,1     while n<max:         print(b)          a,b=b,a+b         n=n+1     return ‘done‘     >>> fib(6)112358‘done‘


要把 上面的 fib 函數改為 generator 僅需要把 print(b) 改為  yield b 就可以了:

>>> def fib(max):     n,a,b=0,0,1      while n<max:                           yield b                    a,b=b,a+b                           n=n+1           return ‘done‘      >>> f = fib(6)>>> f<generator object fib at 0x02C9FD28>>>> for n in f:     print(n)112358


最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。


舉個簡單的例子,定義一個generator,依次返回數字1,3,5:

>>> def odd():     print(1)     yield 1     print(2)     yield 3     print(3)     yield 5

    

>>> o=odd()>>> next(o)11>>> next(odd())11>>> next(o)23>>> next(o)35>>> next(o)Traceback (most recent call last):  File "<pyshell#104>", line 1, in <module>    next(o)StopIteration

可以看到,odd不是普通函數,而是generator,在執行過程中,遇到yield就中斷,下次又繼續執行。執行3次yield後,已經沒有yield可以執行了,所以,第4次調用next(o)就報錯。


但是用for迴圈調用generator時,發現拿不到generator的return語句的傳回值。如果想要拿到傳回值,必須捕獲StopIteration錯誤,傳回值包含在StopIterationvalue中:

>>> g=fib(6)>>> while True:     try:                   x=next(g)                   print(‘g:‘,x)          except StopIteration as e:         print(‘Generator return value:‘,e.value)                   break    g: 1g: 1g: 2g: 3g: 5g: 8Generator return value: done


迭代器

我們已經知道,可以直接作用於for迴圈的資料類型有以下幾種:

一類是集合資料類型,如listtupledictsetstr等;

一類是generator,包括產生器和帶yield的generator function。

這些可以直接作用於for迴圈的對象統稱為可迭代對象:Iterable

可以使用isinstance()判斷一個對象是否是Iterable對象:

>>> from collections import Iterable>>> isinstance([],Iterable)True>>> isinstance({},Iterable)True>>> isinstance(‘abc‘,Iterable)True>>> isinstance((x for x in range(10)),Iterable)True>>> isinstance(100,Iterable)False



而產生器不但可以作用於for迴圈,還可以被next()函數不斷調用並返回下一個值,直到最後拋出StopIteration錯誤表示無法繼續返回下一個值了。

可以被next()函數調用並不斷返回下一個值的對象稱為迭代器:Iterator

可以使用isinstance()判斷一個對象是否是Iterator對象:

>>> from collections import Iterator >>> isinstance((x for x in range(10)),Iterator)True>>> isinstance([],Iterator)False>>> isinstance({},Iterator)False>>> isinstance(‘asd‘,Iterator)False


產生器都是Iterator對象,但listdictstr雖然是Iterable,卻不是Iterator。把listdictstrIterable變成Iterator可以使用iter()函數:

>>> isinstance(iter([]),Iterator)True>>> isinstance(iter({}),Iterator)True


為什麼list、dict、str、等資料類型不是Iterator?

這是因為Python的Iterator對象表示的是一個資料流,lterator 對象可以被 next() 函數調用並不斷返回下一個資料,知道沒有資料拋出 stopIteration 錯誤。可以吧這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過函數next()函數實現按需計算下一格資料,所以 Iterator 的計算是惰性的,只有在需要返回下一個資料時他才會計算。

Iterator甚至可以表示一個無限大的資料流,例如全體自然數。而使用list是永遠不可能儲存全體自然數的。


高階函數

一個最簡單的高階函數:

>>> def add(x,y,f):                 return f(x)+f(y)            >>> add(-5,6,abs)11


map()/ reduce()


map()函數接受兩個參數,一個是函數,一個是Iterable,map 將傳入的函數依次作用到序列的每個元素,並把結果作為新的 Iterator 返回。


我們要把函數 f(x)= x2,要把這個函數作用在一個list[1,2,3,4,5,6,7,8,9]上,就可以用map()實現如下:

>>> r=map(f,[1,2,3,4,5,6,7,8,9])>>> next(r)1>>> list(r)[4, 9, 16, 25, 36, 49, 64, 81]
>>> list(map(str,[1,2,3,4,5,6,7,8,9]))[‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘]



reduce() --->  reduce 把一個函數作用在一個序列[x1,x2,x3,.....]上,這個函數必須接受兩個參數,reduce 把結果繼續和序列的下一個元素做積累計算,其效果就是:

 

reduce(f,[x1,x2,x3,x4])  =f(f(f(x1,x2),x3),x4)


計算一個序列的和,就可以用到reduce實現:

>>> from functools import reduce>>> def add(x,y):             return x+y
>>> reduce(add,[1,3,5,7,9])25


當然也可以用python內建函數sum(),沒必要用reduce。

>>> sum([1,3,5,7,9])25


如果把序列[1,3,5,7,9]變換成整數13579.reduce就可以派上用場:

>>> from functools import reduce>>> def fn(x,y):     return x*10+y
>>> reduce(fn,[1,3,5,7,9])13579


filter -----> Python內建的filter()函數用於過濾序列。

filter()也接受一個函數和一個序列。和map()不同的是,filter()把傳入的函數依次作用於每個元素,然後根據傳回值是True還是False決定保留還是丟棄該元素。

eg:在一個list中,刪掉偶數,只保留奇數:

>>> def is_odd(n):     return n%2==1
>>> list(filter(is_odd,[1,2,3,4,5,6,7,8,9]))[1, 3, 5, 7, 9]

刪除字串中的Null 字元:

>>> def not_empty(s):     return s and s.strip()
>>> list(filter(not_empty,[‘A‘,‘‘,‘B‘,None,‘C‘,‘   ‘]))[‘A‘, ‘B‘, ‘C‘]



回數是指從左向右讀和從右向左讀都是一樣的數,例如12321909。請利用filter()濾掉非回數:

>>> print(list(filter(lambda n:str(n) == str(n)[::-1], range(1, 2000))))#先把數字轉換為字串,然後翻轉字串,最後比較[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242,252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383,393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525,535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666,676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808,818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949,959, 969, 979, 989, 999, 1001, 1111, 1221, 1331, 1441, 1551, 1661, 1771, 1881, 1991]


本文出自 “啟思·朝聖者” 部落格,請務必保留此出處http://dearch.blog.51cto.com/10423918/1761661

據廖雪峰python3教程----python學習第十天

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.