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為基準)