#!/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)
Python迭代器與生成器實例演示
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.