1. 程式人生 > >python迭代器&生成器使用技巧(2):切片、遍歷、索引值、多序列、多容器物件

python迭代器&生成器使用技巧(2):切片、遍歷、索引值、多序列、多容器物件

1. 迭代器切片

迭代器和生成器不能使用標準的切片操作,因為它們的長度事先並不知道(並且也沒有實現索引)。 函式 islice() 返回一個可以生成指定元素的迭代器,通過遍歷並丟棄直到切片開始索引位置的所有元素,然後開始一個個的返回元素,並直到切片結束索引位置。

import itertools
def count(n):
    while True:
        yield n 
        n += 1

c = count(0)
# print( c[10:20] ) 
# TypeError: 'generator' object is not subscriptable

for num in itertools.islice(c, 10, 15):
    print(num)
# >>> 10, 11, 12, 13, 14

這裡需要強調的一點是 islice() 會消耗掉傳入的迭代器中的資料。 而迭代器是不可逆的。 所以如果需要之後再次訪問這個迭代器的話,就得先將它裡面的資料放入一個列表中了。

2. 排列組合的迭代遍歷

如果我們想迭代遍歷集合中元素的所有可能的排列或組合,可以用itertools模組提供兩函式來解決這類問題。

  • itertools.permutations() , 接受集合併產生一個元組序列,每個元組由集合中所有元素的一個可能排列組成。 也就是說通過打亂集合中元素排列順序生成一個元組。如果想得到指定長度的排列,可以傳遞長度引數permutation(items, length)。
from itertools import permutations

items = ['a', 'b', 'c']
for sample in permutations(items):
    print( sample )
# >>> ('a', 'b', 'c')
#     ('a', 'c', 'b')
#     ('b', 'a', 'c')
#     ('b', 'c', 'a')
#     ('c', 'a', 'b')
#     ('c', 'b', 'a')
  • itertools.combinations() 得到輸入集合中元素的所有的組合
from itertools import combinations
for sample in combinations(items, 3):
    print(sample)
# >>> ('a', 'b', 'c')

對於 combinations() ,元素的順序已經不重要了。 也就是說,組合 ('a', 'b') 跟 ('b', 'a') 其實是一樣的.

3. 序列上索引值迭代 enumerate()

如果我們想在迭代一個序列的同時跟蹤正在被處理的元素索引時可以採用這種方法。這種情況在遍歷檔案時想在錯誤訊息中使用行號定位時候非常有用。

my_list = ['a', 'b', 'c']
for idx, val in enumerate(my_list):
    print(idx, val)
# >>> 0 a
#     1 b
#     2 c

遍歷檔案訊息時,可以直接用於發現檔案訊息發生錯誤所處的位置(行號):

def parse_data(filename):
    with open(filename, 'rt') as f:
        for idx, line in enumerate(f,1): # from Line 1
            fields = line.split()
            try:
                # cnt = int (fields[1])
                pass
            except ValueError as e:
                print('Line{}:Parse error: {}'.format(idx, e))

parse_data('test.txt')

4. 同時迭代多個序列 zip()

如何同時迭代多個序列,每次分別從一個序列中取一個元素?zip()函式就是為了解決多個序列同時迭代而設計的。

  • zip() 最小迭代終止
xpts = [1, 5, 4, 2, 10]
ypts = [101, 78, 37, 15, 62, 99,56, 22]
for x, y in zip(xpts, ypts):
    print(x,y)
# >>> 1 101
#     5 78
#     4 37
#     2 15
#     10 62

zip(a, b) 會生成一個可返回元組 (x, y) 的迭代器,其中x來自a,y來自b。 一旦其中某個序列到底結尾,迭代宣告結束。 因此迭代長度跟引數中最短序列長度一致

  • zip_longest() 最大迭代終止
from itertools import zip_longest

xpts = [1, 5, 4, 2, 10]
ypts = [101, 78, 37, 15, 62, 99, 56, 22]
for x, y in zip_longest(xpts, ypts):
    print(x,y)
# >>> 1 101
#     5 78
#     4 37
#     2 15
#     10 62
#    None 99
#    None 56
#    None 22

zip() 函式對於同時處理兩個、兩個以上的序列非常有用。需要注意的一點是:zip() 會建立一個迭代器來作為結果返回。 如果需要將結對的值儲存在列表中,要使用list() 函式

xpts = [1, 5, 4, 2, 10]
ypts = [101, 78, 37, 15, 62, 99, 56, 22]
print(zip(xpts, ypts))
# >>> <zip object at 0x0000000007505F88>
print(list(zip(xpts, ypts)))
# >>> [(1, 101), (5, 78), (4, 37), (2, 15), (10, 62)]

5. 不同集合上元素的迭代

如果我們想在多個物件執行相同的操作,但是這些物件在不同的容器中。在不失可讀性的情況下如何避免寫重複的迴圈?itertools.chain() 可以用來簡化這個任務。 它接受可迭代物件列表作為輸入,並返回一個迭代器,有效的遮蔽掉在多個容器中迭代細節。 

from itertools import chain

a = [1, 2]
b = ['x', 'y', 'z']
for x in chain(a, b):
    print(x)
# >>> 1 2 x y z
    
print(chain(a, b))
# >>> <itertools.chain object at 0x0000000008B18978>

print(list(chain(a,b)))
# >>> [1, 2, 'x', 'y', 'z']

itertools.chain() 接受一個或多個可迭代物件作為輸入引數。 然後建立一個迭代器,依次連續的返回每個可迭代物件中的元素。 這種方式(chain(a,b))要比先將序列合併(a+b)再迭代要高效的多。a + b 操作會建立一個全新的序列並要求a和b的型別一致。 chian() 不會有這一步,所以如果輸入序列非常大的時候會很省記憶體。 並且當可迭代物件型別不一樣的時候 chain() 同樣可以很好的工作。

文章參考《python3-cookbook》