Python3學習筆記 | 十六、Python的語句與語法-迭代器和解析(1)
- 2019 年 10 月 6 日
- 筆記
一、迭代器
1、初探
之前章節中,我們看到for語句可以Python任何序列類型,包括列表、元祖以及字元串。如下所示:
>>> for i in [1,2,3]:print(i,end=' ') ... 1 2 3 >>> for i in (1,2,3):print(i,end=' ') ... 1 2 3 >>> for i in '123':print(i,end=' ') ... 1 2 3
下面我們繼續看文件迭代器、手動迭代(iter, next)、其它內置類型迭代器。
2、文件迭代器
回憶一下之前章節,文件訪問方式有如下:
.read(): 一次性讀取全部內容。
.readline(): 一次讀取一行。
.readlines(): 生成列表,每一行是每個元素。
.next(): 跟readline()差不多,但讀取完之後報錯。 next()報錯,為StopIteration。在Python中任何這類對象都認為是可迭代的。在Python里迭代工具(比如for)會調用next()來獲取數據,並以StopIteration來確認何時離開。
for line in open(<filename>): print(line, end = '')
因為每個元素在最後包涵n(換行符),因此print函數結尾需要取消結尾中的換行。
註: 盡量不要使用readlines()函數,因為這個會一次性的把所有內容讀取到記憶體里(轉換為列表),運行速度會比較慢。最好使用readline或者迭代文件方法。
3、手動迭代
為了支援手動迭代程式碼,Python支援next()函數,它會自動讀取next()函數。next(X)等同於X.next()。
f = open(<filename>) next(f)
這個會從第一行開始讀取內容。
從技術角度來講,迭代協議里,當使用for函數進行迭代時,會傳遞給iter內置函數,以便可迭代對象中獲取迭代器。返回的對象有需要有next()方法。
>>> L = [1,2,3] >>> i = iter(L) >>> next(i) 1 >>> next(i) 2 >>> next(i) 3 >>> next(i) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
對於文件來說,不需要轉換成iter類型的這一步:
>>> file = open(r'C:Testtest.txt') >>> file is iter(file) True
因為文件對象就是自己的迭代器,但列表,元祖,字元串等就不是了。
>>> str = '123' ; list = [1,2,3] ; tuple = (1,2,3) >>> str is iter(str) False >>> list is iter(list) False >>> tuple is iter(tuple) False
4、其他內置類型迭代器
除了文件以及像列表這樣的實際的序列外,其它類型也有其適用的迭代器。例如,遍歷字典鍵的經典方法是明確地獲取其鍵的列表。
>>> dict = {'a': 1 ,'b' : 2 ,'c' : 3} >>> for key in dict.keys() : ... print(key,dict[key]) ... a 1 b 2 c 3
二、列表解析
1、初探
遍歷列表時,使用for循環來修飾它:
>>> list = [1,2,3] >>> for i in range(len(list)) : ... list[i] += 10 ... >>> list [11, 12, 13]
這個是有效的,但不是「最佳實踐」。我們可以使用產生所需結果列表的一個單個表達式來完成上面循環:
>>> list = [1,2,3] >>> list = [i + 10 for i in list] >>> list [11, 12, 13]
2、列表解析基礎知識
從之前的例子開始分析:list1 = [i + 10 for i in list1]
這個先是運算 [i + 10 for i in list1]之後,再把此賦值給list1。我們來看裡面是如何運算:
先是對list1進行迭代,每次把單個值符給i,再進行i + 10,成為新列表的單個元素。這個相當於:
tmp = [] for i in list1: tmp.append(i+10) list1 = tmp
可以認為是倒過來的for循環語句。
3、在文件上使用列表解析
>>> file = open(r'C:Testtest.txt').readlines() >>> file ['DoraEmonn', 'Daxiongn', 'JingXiang']
在這裡發現,每行最後都會有n換行符,這個時候我們可以使用列表解析來進行去除換行符的操作。
>>> file = [line.strip() for line in file] >>> file ['DoraEmon', 'Daxiong', 'JingXiang']
或更簡單
>>> file = [line.strip() for line in open(r'C:Testtest.txt')]
還可以進行更多操作:
>>> file = [line.strip().upper() for line in open(r'C:Testtest.txt')] >>> file ['DORAEMON', 'DAXIONG', 'JINGXIANG']
4、擴展的列表解析語法
擴展語法是在循環基礎上,添加了條件:
>>> file = [line.strip().upper() for line in open(r'C:Testtest.txt') if line[0] !='J'] >>> file ['DORAEMON', 'DAXIONG']
在這裡,發現添加 if line[0] != J』的時候,不顯示以 「 J 「 開頭的行。
>>> [x+y for x in [1,2,3] for y in [10,20,30]] [11, 21, 31, 12, 22, 32, 13, 23, 33]
我們會發現第二個for循環算是嵌套在第一個for循環。
三、其他迭代環境
map也可用在迭代中:
>>> list(map(str.upper, open(r'C:Testtest.txt'))) ['DORAEMONn', 'DAXIONGn', 'JINGXIANG']
map函數是把後面的可迭代的每個值當作前面的參數傳入。在Python3開始正式引入map,之前版本,python2.7也可以使用,但map可以直接返回列表,不需要使用list函數進行轉換。後續章節中會繼續講解。上面的可以如下解釋:
>>> tmp = [] >>> for line in open(r'C:Testtest.txt'): ... tmp.append(str.upper(line)) ... >>> tmp ['DORAEMONn', 'DAXIONGn', 'JINGXIANG']
相應的,也有sorted,會對迭代對象進行排序後生成列表。
>>> sorted(open(r'C:Testtest.txt')) ['Daxiongn', 'DoraEmonn', 'JingXiang']
enumerate也會對迭代對象進行運算後,生成可迭代對象。
>>> list(enumerate(open(r'C:Testtest.txt'))) [(0, 'DoraEmonn'), (1, 'Daxiongn'), (2, 'JingXiang')]
enumerate就是在原有的順序中添加序列號。除了這些,還有filter與reduce方法,這些再後續章節講解用法。
>>> list(filter(bool,open(r'C:Testtest.txt'))) ['DoraEmonn', 'Daxiongn', 'JingXiang'] >>> import operator,functools >>> functools.reduce(operator.add,open(r'C:Testtest.txt')) 'DoraEmonnDaxiongnJingXiang'
sum、any、all、max、min也可使用迭代器。
any() 函數用於判斷給定的可迭代參數 iterable 是否全部為 False,則返回 False,如果有一個為 True,則返回 True。元素除了是 0、空、FALSE 外都算 TRUE。
all() 函數用於判斷給定的可迭代參數 iterable 中的所有元素是否都為 TRUE,如果是返回 True,否則返回 False。元素除了是 0、空、None、False 外都算 True。
>>> sum((1,2,3)),sum([4,5,6]) (6, 15) >>> any([1,[],'True']), all([1,[],'True']) (True, False) >>> max([2,3,1]),min([2,3,1]) (3, 1)
list, tuple, join 都是可以對可迭代對象進行操作後輸出:
>>> list(open(r'C:Testtest.txt')),tuple(open(r'C:Testtest.txt')),'**'.join(open(r'C:Testtest.txt')) (['DoraEmonn', 'Daxiongn', 'JingXiang'], ('DoraEmonn', 'Daxiongn', 'JingXiang'), 'DoraEmonn**Daxiongn**JingXiang')
四、Python3中新的迭代環境
從Python3.x開始,更注重迭代。在Python2.x里,很多函數生成的是列表方式:
>>> zip('abc','123') [('a', '1'), ('b', '2'), ('c', '3')]
但在Python3.x開始是變成可迭代的特定對象:
>>> zip('abc','123') <zip object at 0x102295148> >>> list(zip('abc','123')) [('a', '1'), ('b', '2'), ('c', '3')]
Python 3.x的這種方式,會延遲計算,在提取內容的時候計算結果。這樣會節省記憶體空間,不需要提前計算後放進記憶體里。迭代對象,當迭代完成之後,不能再次讀取。
>>> a = list(zip('abc','123')) >>> for i in a: print(i, end=' ') ... ('a', '1') ('b', '2') ('c', '3') >>> >>> for i in a: print(i, end=' ') ... >>>
1、range
range從Python3.x開始變為迭代器。 Python 2.x:
>>> range(5) [0, 1, 2, 3, 4]
Python 3.x:
>>> range(5) range(0, 5)
所以,在Python 3.x要是需要生成一個有序列表,需要再次轉換:
>>> list(range(5)) [0, 1, 2, 3, 4]
但也可以像列表一樣使用:
>>> range(5)[1] 1 >>> range(5)[-1] 4
註:range可繼續使用。
>>> r = range(7) >>> for i in r : ... print(i,end = ' ') ... 0 1 2 3 4 5 6 >>> >>> for i in r : ... print(i,end = ' ') ... 0 1 2 3 4 5 6 >>>
2、map、zip和filter
在Python3.x里,map、zip和filter也是使用迭代器來節約記憶體開銷。但與range不同,使用一次之後,就不能再次使用。
>>> M = map(abs,(-1,3,7)) >>> list(M) [1, 3, 7] >>> list(M) [] >>> Z = zip((1,2,3),('a','b','c')) >>> list(Z) [(1, 'a'), (2, 'b'), (3, 'c')] >>> list(Z) [] >>> F = filter(bool,(1,'','True')) >>> list(F) [1, 'True'] >>> list(F) []
map() 函數會根據提供的函數對指定序列做映射。第一個參數 function 以參數序列中的每一個元素調用 function 函數,返回包含每次 function 函數返回值的新列表。
zip() 函數用於將可迭代的對象作為參數,將對象中對應的元素打包成一個個元組,然後返回由這些元組組成的對象,這樣做的好處是節約了不少的記憶體。我們可以使用 list() 轉換來輸出列表。如果各個迭代器的元素個數不一致,則返回列表長度與最短的對象相同,利用 * 號操作符,可以將元組解壓為列表。
filter() 函數用於過濾序列,過濾掉不符合條件的元素,返回一個迭代器對象,如果要轉換為列表,可以使用 list() 來轉換。該接收兩個參數,第一個為函數,第二個為序列,序列的每個元素作為參數傳遞給函數進行判,然後返回 True 或 False,最後將返回 True 的元素放到新列表中。
五、多個迭代器 vs 單個迭代器
之前看到的range,可以同時使用多個迭代器。這個叫range有著多個迭代器。可以進行索引。但其它的,只能迭代一次。
後續章節會看到各種迭代器,而且會說明如何生成兩種迭代器。
六、字典視圖迭代器
字典視圖迭代器,與其它多個迭代器相似(在Python2.x里還是使用列表)
>>> dict = {'a':1,'b':2,'C':3} >>> dict.keys() dict_keys(['a', 'b', 'C']) >>> dict.values() dict_values([1, 2, 3]) >>> dict.items() dict_items([('a', 1), ('b', 2), ('C', 3)])
在這裡,迭代器都算是多個迭代器(Python3.4為基準)