1. 程式人生 > >python迭代器&生成器使用技巧(1):遍歷、代理、生成器建立迭代、反向迭代

python迭代器&生成器使用技巧(1):遍歷、代理、生成器建立迭代、反向迭代

1. 手動遍歷迭代器 next()

遍歷一個可迭代物件中的所有元素,但是卻不想使用for迴圈。為了手動的遍歷可迭代物件,使用 next() 函式並在程式碼中捕獲 StopIteration 異常。 通常來講, StopIteration 用來指示迭代的結尾。 然而,如果手動使用 next() 函式的話,還可以通過返回一個指定值來標記結尾,比如 None 。讀取一個檔案的所有行例項。

def manual_iter(filename):
    with open(filename) as f:
        try:
            while True:
                line = next(f)
                print(line, end = '')
        except StopIteration:
            pass
        
manual_iter(filename = 'test.txt')      

############################

with open('test.txt') as f:
    while True:
        line = next(f, None)
        if line is None:
            break
        print(line, end='')
# >>> sorry
#     i really don't know how to choose?
#     go abroad, tencent, or alibaba?
#     but anyway,
#     i absolutely don't choose to huawei...    

迭代機制:獲取迭代器+執行迭代器

items = [1, 2, 3]      
# Get the iterator
it = iter(items) # Invokes items.__iter__()
# Run the iterator
print( next(it) ) # 1 
print( next(it) ) # 2 
print( next(it) ) # 3 
print( next(it) ) # Traceback : StopIteration

2. 代理迭代

有的時候,我們需要構建自定義容器物件,裡面包含有列表、元組或其他可迭代物件。並且直接在新容器物件上執行迭代操作。實際上我們只需要定義一個__iter__()方法,將迭代操作代理到容器內部的物件上去。

class Node:
    def __init__(self, value):
        self.value = value
        self._children = []
    
    def __repr__(self):
        return 'Node({!r})'.format(self.value)

    def add_child(self, node):
        self._children.append(node)
        
    def __iter__(self):
        return iter(self._children)

if __name__ == '__main__':
    root = Node(0)
    child1 = Node(1)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    
    for tar in root:
        print(tar)
# >>> Node(1) Node(2) 

在上面程式碼中, __iter__() 方法只是簡單的將迭代請求傳遞給內部的 _children 屬性。

3. 使用生成器建立新的迭代模式

如果想實現一種新的迭代模式,可以使用生成器函式來定義。 下面是一個生產某個範圍內浮點數的生成器:

def frange(start, stop, increment):
    x = start
    while x < stop:
        yield x       # pointer to next object
        x += increment
        
for num in frange(0, 4, 0.5):
    print(num)
# >>> 0 0.5 1.0 1.5 2.0 2.5 3.0 3.5

一個函式中需要有一個 yield 語句即可將其轉換為一個生成器。 跟普通函式不同的是,生成器只能用於迭代操作。一個生成器函式主要特徵是它只會迴應在迭代中使用到的 next 操作。 一旦生成器函式返回退出,迭代終止。

4. 反向迭代

當我們需要反向迭代時,就需要reversed()函式進行處理。

a = [1, 2, 3, 4]
for num in reversed(a):
    print (num)
# >>> 4 3 2 1

反向迭代僅僅當物件的大小可預先確定或者物件實現了 __reversed__() 的特殊方法時才能生效。 如果兩者都不符合,那必須先將物件轉換為一個列表才行,比如:

"""
a = [1, 2, 3, 4]
for num in reversed(a):
    print (num)
# >>> 4 3 2 1
"""
f = open('test.txt')
for line in reversed(list(f)):
    print(line, end='')
# >>> i absolutely don't choose to huawei.but anyway.
#     go abroad, tencent, or alibaba?
#     i really don't know how to choose?
#     sorry.
  • 自定義類上實現 __reversed__() 方法來實現反向迭代
class Countdown:
    def __init__(self, start):
        self.start = start

    def __iter__(self):# Forward
        n = self.start
        while n > 0:
            yield n
            n -= 1
    
    def __reversed__(self):# Reverse
        n = 1
        while n <= self.start:
            yield n
            n += 1

for rr in reversed(Countdown(30)):
    print(rr)
for rr in Countdown(30):
    print(rr)

定義一個反向迭代器可以使得程式碼非常的高效, 因為它不再需要將資料填充到一個列表中然後再去反向迭代這個列表。

文章參考《python3-cookbook》