實現迭代協議

對於這種需求,一般來說我們都是使用生成器函數,我們可以使用Node類來表示樹結構,以深度優先的模式來遍歷所有樹節點:

#建立樹結構
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)

實際上,這個代碼邏輯就是先產生出自身,然後迭代每個子節點,利用子節點的deph_first()方法產生出其它元素。

在Python中,迭代協議要求__iter__()返回一個特殊的迭代器對象,這個對象必須要實現__next__()方法,並且使用StopIteration來通知迭代的完成。因此,上面的代碼還可以這樣寫:

class Node2:
#迭代
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):
return DepthFirstIterator(self)

#深度優先搜索遍歷所有節點
class DepthFirstIterator(object):

Depth-first traversal

def __init__(self, start_node):
self._node = start_node
self._children_iter = None
self._child_iter = None

def __iter__(self):
return self

def __next__(self):
#為子結點創建迭代器
if self._children_iter is None:
self._children_iter = iter(self._node)
return self._node
# If processing a child, return its next item
elif self._child_iter:
try:
nextchild = next(self._child_iter)
return nextchild
except StopIteration:
self._child_iter = None
return next(self)
# 前進到下一個子節點並開始迭代
else:
self._child_iter = next(self._children_iter).depth_first()
return next(self)

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)

反向迭代

前面我們講了迭代器的使用,但很多人不知道的是,它還可以進行反向迭代,只需要使用內建的reversed()函數進行反向迭代就好,舉個例子:

def reverse_iterate():
a = [1, 2, 3, 4]
for x in reversed(a):
print(x)

反向迭代只有在待處理的對象擁有可確定的大小,或者是對象實現了_reversed_()特殊方法的時候才能奏效。如果這兩個條件都無法滿足,你就必須把這個對象轉化成列表再進行反迭代。但注意,再反向迭代器中,像這樣的操作會消耗大量的內存,程序就會運行非常的緩慢,尤其是迭代對象比較大的時候特別顯著

許多人其實都沒意識到如果他們實現了_reversed_()方法,實際上在自定義類中反向迭代的操作也是可以的。

class Countdown:
def __init__(self, start):
self.start = start

# 正向迭代器
def __iter__(self):
n = self.start
while n > 0:
yield n
n -= 1

# 反向迭代器
def __reversed__(self):
n = 1
while n <= self.start:
yield n
n += 1

if __name__ == __main__:
reverse_iterate()

定義帶有額外狀態的生成器函數

如果想讓生成器的狀態暴露給用戶,我們可以把它實現成為一個類,然後把生成器函數放到_iter_()裏就好:

from collections import deque

class linehistory:
def __init__(self, lines, histlen=3):
self.lines = lines
self.history = deque(maxlen=histlen)

def __iter__(self):
for lineno, line in enumerate(self.lines, 1):
self.history.append((lineno, line))
yield line

def clear(self):
self.history.clear()

要使用這個類,可以將它看成一個普通的生成器函數。但是,由於它會創建一個類實例,所以可以訪問內部屬性。比如說History或者clear()

def gen_extrastate():
with open(somefile.txt) as f:
lines = linehistory(f)
for line in lines:
if python in line:
for lineno, hline in lines.history:
print({}:{}.format(lineno, hline), end=)

if __name__ == __main__:
gen_extrastate()

參考書目

《Python CookBook》作者:【美】 David Beazley, Brian K. Jones

Github地址:

yidao620c/python3-cookbook?

github.com圖標
推薦閱讀:

相關文章