Python迭代器與生成器實例演示

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : Peidong
# @Site    : 
# @File    : iter_and_generate.py
# @Software: PyCharm

# 迭代器與生成器

# 1.1 手動遍歷迭代器
# 使用next() 函數並在代碼中捕獲StopIteration 異常
items = [1, 2, 3, 4]
# 獲取迭代
it = iter(items)
print(next(it)) #1
print(next(it)) # 2
print(next(it)) # 3
print(next(it)) # 4

# 1.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 ch in root:
        print(ch)

# 1.3 使用生成器創建新的迭代模式
def frange(start, stop, increment):
    x = start
    while x < stop:
        yield x
        x += increment
for n in frange(0, 4, 0.5):
    print(n)
print(list(frange(0, 4, 0.5)))
# 生成器智能用於迭代操作,一個生成器函數主要特徵是它只會迴應在迭代中使用到的next 操作。一旦生成器
# 函數返回退出,迭代終止。
def countdown(n):
    print('Starting to count from', n)
    while n > 0:
        yield n
        n -= 1
    print('Done!')

c = countdown(3)
print(c)
print(next(c)) # 3
print(next(c)) # 2
print(next(c)) # 1
print(next(c)) # Done

# 1.4 實現迭代器協議
# 在一個對象上實現迭代最簡單的方式是使用一個生成器函數
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)

    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()

# 實例
if __name__ == '__main__':
    root = Node(0)
    child1 = Node(1)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    child1.add_child(Node(3))
    child1.add_child(Node(4))
    child2.add_child(Node(5))
    for ch in root.depth_first():
        print(ch)

# 1.4 反向迭代
# 使用內置的reversed()函數
a = [1, 2, 3, 4]
for x in reversed(a):
    print(x)
# 反向迭代僅僅當對象的大小可預先確定或者對象實現了reversed () 的特殊方
# 法時才能生效。如果兩者都不符合,那你必須先將對象轉換爲一個列表才行
f = open('someFile')
for line in reversed(list(f)):
    print(line, end='')
在自定義類上實現__reversed__()方法來實現反向迭代
class Countdown:
    def __init__(self, start):
        self.start = start

    # Forward iterator
    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1

    # Reverse iterator
        def __reversed__(self):
            n = 1
            while n <= self.start:
                yield n
                n += 1
for rr in Countdown(30):
    print(rr)

# 1.5 迭代器切片
# 函數itertools.islice() 正好適用於在迭代器和生成器上做切片操作
def count(n):
    while True:
        yield n # 生成器
        n += 1
c = count(0)

import itertools
for x in itertools.islice(c, 10, 20):
    print(x, end=' ')
# 迭代器和生成器不能使用標準的切片操作,因爲它們的長度事先我們並不知道(並
# 且也沒有實現索引)。函數islice() 返回一個可以生成指定元素的迭代器,它通過遍
# 歷並丟棄直到切片開始索引位置的所有元素。然後纔開始一個個的返回元素,並直到
# 切片結束索引位置。
# 這裏要着重強調的一點是islice() 會消耗掉傳入的迭代器中的數據。必須考慮到
# 迭代器是不可逆的這個事實。所以如果你需要之後再次訪問這個迭代器的話,那你就
# 得先將它裏面的數據放入一個列表中。

# 1.6 排列組合的迭代
# 迭代遍歷一個集合中元素的所有可能的排列或組合
# itertools 模塊提供了三個函數來解決這類問題。其中一個是
# itertools.permutations() , 它接受一個集合併產生一個元組序列, 每個元組
# 由集合中所有元素的一個可能排列組成
items = ['a', 'b', 'c']
from itertools import permutations
for p in permutations(items):
    print(p)
# 如果你想得到指定長度的所有排列,你可以傳遞一個可選的長度參數
for p in permutations(items, 2):
    print(p)
# 使用itertools.combinations() 可得到輸入集合中元素的所有的組合
from itertools import combinations
for c in combinations(items, 3):
    print(c)
for c in combinations(items, 2):
    print(c)
for c in combinations(items, 1):
    print(c)
# 在計算組合的時候, 一旦元素被選取就會從候選中剔除掉(比如
# 如果元素’a’ 已經被選取了, 那麼接下來就不會再考慮它了)。而函數
# itertools.combinations with replacement() 允許同一個元素被選擇多次
for c in combinations_with_replacement(items, 3): # 命令有問題
    print(c)

# 1.7 序列上索引值迭代
# 問題:想在迭代一個序列的同時跟蹤正在被處理的元素索引
# 解決:內置的enumerate() 函數可以很好的解決這個問題
my_list = ['a', 'b', 'c', 'd']
for idx, val in enumerate(my_list):
    print(idx, val, end=' ') # 0 a 1 b 2 c 3 d
# 爲了按傳統行號輸出(行號從1 開始),你可以傳遞一個開始參數
for idx, val in enumerate(my_list, 1):
    print(idx, val, end=' ') # 1 a 2 b 3 c 4 d

# 1.8 同時迭代多個序列
# 同時迭代多個序列,每次分別從一個序列中取一個元素
# 解決:同時迭代多個序列,使用zip() 函數
xpts = [1, 5, 4, 2, 10, 7]
ypts = [101, 78, 37, 15, 62, 99, 100, 123]
for x, y in zip(xpts, ypts):
    print(x, y)
# 迭代長度與參數中最短序列長度一致
# 如果這個不是你想要的效果,那麼還可以使用itertools.zip longest() 函數來代替
from  itertools import zip_longest
for i in zip_longest(xpts, ypts):
    print(i, end=' ')
# 成對處理數據的時候zip() 函數是很有用的
headers = ['name', 'shares', 'price']
values = ['ACME', 100, 490.1]
# 使用zip() 可以讓你將它們打包並生成一個字典
s = dict(zip(headers,values))
print(s)
# 成對輸出
for name, val in zip(headers, values):
    print(name, '=', val)
# 多組數同時輸出
a = [1, 2, 3]
b = [10, 11, 12]
c = ['x','y','z']
for i in zip(a, b, c):
    print(i)
# zip() 會創建一個迭代器來作爲結果返回。如果你需要將結對的值存儲在列表中,要使用list() 函數

# 1.9 不同集合上元素的迭代
# 你想在多個對象執行相同的操作,但是這些對象在不同的容器中,你希望代碼在不
# 失可讀性的情況下避免寫重複的循環。
# itertools.chain() 方法可以用來簡化這個任務
from itertools import chain
a = [1, 2, 3, 4]
b = ['x', 'y', 'z']
for x in chain(a, b):
    print(x)

# 1.10 展開嵌套的序列
from collections import Iterable
def flatten(items, ingore_types = (str, bytes)):
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, ingore_types):
            yield from flatten(x)
        else:
            yield x
items = [1, 2, [3, 4, [5, 6], 7], 8]
for x in flatten(items):
    print(x, end=' ') # 1 2 3 4 5 6 7 8
# 在上面代碼中, isinstance(x, Iterable) 檢查某個元素是否是可迭代的。如果是的話
# , yield from 就會返回所有子例程的值
# 額外的參數ignore types 和檢測語句isinstance(x, ignore types) 用來將字符
# 串和字節排除在可迭代對象外,防止將它們再展開成單個的字符
items = ['Dave', 'Paula', ['Thomas', 'Lewis']]
for x in flatten(items):
    print(x)

# 1.11 順序迭代合併後的排序迭代對象
# 問題:你有一系列排序序列,想將它們合併後得到一個排序序列並在上面迭代遍歷。
# 解決:heapq.merge() 函數可以幫你解決這個問題
import heapq
a = [1, 4, 7, 10]
b = [2, 5, 10, 13]
for c in heapq.merge(a, b):
    print(c)


發佈了110 篇原創文章 · 獲贊 65 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章