Python自學記錄——列表生成式、生成器、迭代器

前段時間因工作問題和家中的電腦無法開機,學習Python的計劃擱置了一段時間。藉此感覺到以前學習的速度過慢,爲了達成自己制定的學習目標,決定延長每次Python的學習時間,增加記錄內容。下面正題開始:

列表生成式

即快速生成 list 的寫法,它內置於Python中,簡單又強大。

舉個例子,生成一個數字1到10的列表,寫法如下:

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

若想得到元素乘積,則列表生成式寫法如下:

[x * x for x in L]
[1,4,9,16,25,36,49,64,81,100]

不用列表生成式的寫法如下:

L1 = []
for x in L :
    L1.append(x * x)
L1
[1,4,9,16,25,36,49,64,81,100]

有此可見,列表生成式簡化了很多代碼,使用起來也十分方便。

繼續往下走,在循環中加一個條件,使列表只得到偶數的乘積,則寫法如下:

[x * x for x in L if x % 2 == 0]
[4,16,36,64,100]

列表生成式的基本寫法如上所示,它還可以內置多個循環,形成全排列,示例如下:

[ x + y for x in 'ABC' for y in 'XYZ']
['AX','AY','AZ','BX','BY','BZ','CX','CY','CZ']

一般情況下,只使用兩層循環,三層及三層以上的循環就很少使用了。列表生成式還可拼接字典dict,示例如下:

D = {'a':'1','b':'2','c':'3'}
[k+'='+v for k,v in D.items()] 
['a=1','c=3','b=2']

由於dict內是無序的,所以輸入的結果順序對應會有些差異,此問題可以忽略不計。

測試題:

L3 = ['Apple','Banana',18,'Orange',None]

上述list,要求把大寫字母變爲小寫字母,錯誤寫法如下:

[x.lower() for x in L3]
Traceback (most recent call last):
  File "<stdin>",line 1,in <module>
AttributeError: 'int' object has no attribute 'lower'

報錯結果,大致說 int 類型的元素不支持 lower 方法,所以我們正確的寫法需要加判斷來篩選下 ,正確示例如下:

[x.lower() if isinstance(x,str) else x for x in L3]
['apple','banana',18,'orange',None]

由此,可以總結出列表表達式的寫法如下:

[執行方法(得到值) 附加條件  循環  簡單判斷條件(可寫可不寫)]

我認爲循環語句的執行過程如下:

1.循環遍歷出第一個值;

2.若有簡單判斷條件進行判斷,若無,跳過這步,進行下一步;

3.若有附加條件,先判斷附加條件,此時寫法上有一點需要注意,if成功的結果會調用執行方法(得到值)的方法對遍歷得到的值進行計算,if 後不需要寫值,else 後需要些對應的執行方法。若只判斷一次,則將判斷條件寫在 簡單判斷條件 的位置上,附加條件不寫,有else的情況 才把判斷都移到前面來,示例如下:

[x.lower() if isinstance(x,str) else x for x in L3]
此爲需要else的寫法
[x.lower() for x in L3 if isinstance(x,str)]
此爲不需要else的寫法

4.把值存放到 list 中

4.循環遍歷下一個值,重複執行步驟2,3,4。

列表生成式就到這裏,下面是生成器。

生成器

在Python中,有一種邊循環邊計算的機制,稱爲 生成器generator。由於它是邊循環邊計算的,所以使用時會節省很多空間。

它的使用方法也很簡單,與 列表生成器 類似,將 [ ] 替換爲 ( ) 即可。與list對比的示例如下:

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

list 創建完成後是一個可見的列表,而generator創建出來的對象不能直接看到它包含的信息,若想打印看到,需要使用next()方法,具體示例如下:

g = (x * x for x in range(10))
next(g)
0
next(g)
1
next(g)
4
next(g)
9
……
next(g)
81
next(g)
Traceback (most recent call last):
  File "<stdin>",line 1, in <module>
StopIteration

由於篇幅有限,循環過程省略了一部分,當next()方法運行到數據之外的時候,會報錯,告訴用戶沒有更多元素了。

若讓我們打印generator運行的數據,只用next()方法來寫,有些尷尬。。正確的方法是用for來遍歷打印,generator也是一個可迭代對象,示例如下:

g1 = (x * x for x in range(10))
for n in g1:
    print(n)

0
1
4
9
……
81

此處聯繫時需注意,不要使用前面的 g 對象,因爲 g 對象中generator 已經全遍歷出來了,for循環時沒有能遍歷出的值,這不會報錯。所以,新建一個 generator 對象,使用for循環遍歷它。結果如上面示例所示。

數學上有個著名的數列,稱爲 斐波拉契數列,它是 除第一個和第二個數外,任意一個數都可由前兩個數相加得到,使用列表生成式無法得到這個數列,但歐陽函數打印出來很容易,示例如下:

def tfb(num):
    n ,a ,b = 0 ,0 ,1
    while n < num:
        print(b)
        a ,b = b ,a + b
        n = n + 1
    return 'end'

tfb(5)

1
1
2
3
5
'end'

上述代碼中,n,a,b = 0,0,1,是創建n,a,b三個對象,並給他們賦值爲0,0,1,即n=0,a=0,b=1。同理,a,b=b,a+b爲a=b,b=a+b。

這個方法和生成器的運行邏輯十分相似,只需要一點改動便可將其變爲generator,即 將 print(b) 替換爲 yield b。這是定義generator的另一種寫法,如果一個函數中包含 yield 關鍵字,那它就不是一個普通的函數,而是一個 generator。示例如下:

def tfl(num):
    n ,a ,b = 0 ,0 ,1
    while n < num:
        yield b
        a ,b = b ,a + b
        n = n + 1
    return 'end'

tfl(5)

<generator object tfl at 0x0000000002116C00>

for x in ftl(5):
    print(x)

1
1
2
3
5

generator和一般的函數執行流程不太相同。一般函數是執行結束後返回需要的指定值;generator 是遇到 yield 就返回指定值,下次執行接着上次 的 yield 執行,執行到首次出現 yield 再返回指定值。具體示例如下:

def g1(num):
    yield num+1
    yield num+2
    yield num+3
    return 100

g = g1(1)

next(g)
2
next(g)
3
next(g)
4
next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

上述示例中會發現,return的值無法被取到,若想要得到return 的值,需要從異常中捕獲StopIteration,從StopIteration.value中取值,具體示例如下:

gg = g1(1)
while True:
    try:
        x = next(gg)
        print('g:',x)
    except StopIteration as e:
        print('return value:',e.value)
        break

g: 2
g: 3
g: 4
return value: end

以楊輝三角爲聯繫,結合百度和自身想要的需求,實現根據輸入數字,返回指定行數的list(默認爲5):

def triangles(num = 5):
    L = [1]
    n = 0
    while n < num:
        yield L
        L.append(0)
        L = [L[i-1] + L[i] for i in range(len(L))]
        n = n+1

for x in triangles():
    print(x)

[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]

迭代器

在此之前,要重申一個概念,叫 可迭代對象Iterable 。

可迭代對象Iterable是指可以通過 for循環 來遍歷出值的對象,例如 list、str、dict等。

而可迭代器Iterator是指可以通過 next() 方法來得到值 的對象,如上述 generator 等。這類的對象都是惰性的,因爲我們只有通過不斷next() 來計算出下一數據。

借他人總結之言,總結下:

1.凡是可作用於for循環的對象都是Iterable類型;

2.凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;

3.集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。

4.Python的for循環本質上就是通過不斷調用next()函數實現的

本篇就到這裏,教材網址:https://www.liaoxuefeng.com, 繼續學習~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章