2015-05-16 121 views
2

在python中編寫一個樹類的迭代器時,我偶然發現了這個問題,我顯然無法訪問母類的字段和方法,而沒有實例化引用已存在的樹實例的迭代器實例,所以我總是需要調用像it.iterate(tree)這樣非常醜陋的迭代器。我想知道是否有某種方法來設計這個東西,所以不需要迭代器及其方法的實例引用。所以,我可以以某種方式從Iterator的實例訪問Tree實例的字段,而無需將Tree實例的引用傳遞給迭代器?Python訪問母類字段

######################################################################## 
# basic tree class, can be traversed bottom up from right hand corner 
######################################################################## 

import sys 

######################################################################## 
######################################################################## 
class Node: 
    """! 
    @brief node class 
    """ 
    def __init__(self, pred=-1, ID=0, label=None): 

     self.sucs = list() 
     self.pred = pred 
     self.ID = ID 
     self.suc_count = 0 
     self.label = label 
######################################################################## 
    def make_suc(self, ID, label=None): 
     """! 
     @brief generates new successor node, assigning it a unique ID 
     """ 
     self.suc_count += 1 
     sucID = ID+1 
     suc = Node(self, sucID, label) 
     self.sucs.append(suc) 
     return suc 
######################################################################## 
######################################################################## 
class Tree: 
    """! 
    @brief tree class 
    """ 
    def __init__(self): 
     self.root = Node() 
     self.allNodes = dict() # mapping from IDs (strings) to nodes 
     self.init() 
     self.leaves = list() 
######################################################################## 
    # initializes node dict 
    def init(self): 
     self.allNodes[0] = self.root 
######################################################################## 
    def find_node(self, ID): 
     """! 
     @brief looks up a node's ID and returns the node itself 
     """ 
     return self.allNodes[ID] 
######################################################################## 
    def add(self, parent, label=None): 
     """! 
     @brief adds a new node under parent with label label 
     """ 
     if parent != Node: 
      parent = self.find_node(parent) 
     suc = parent.make_suc(len(self.allNodes)-1, label) 
     self.allNodes[suc.ID] = suc 
######################################################################## 
    def traverse(self, node): 
     """! 
     @brief traverses tree 
     """ 
     for suc in node.sucs: 
      self.traverse(suc) 
      print suc.label 
######################################################################## 
    def get_leaves(self, node): 
     """! 
     @brief when called resets leveas field and build it up anew by 
       traversing tree and adding all leaves to it 
     """ 
     self.leaves = list() 
     self._find_leaves(node) 
     return self.leaves 
######################################################################## 
    def get_dominated(self, node, dom_nodes=[]): 
     """! 
     @brief finds all dominated nodes 
     """ 
     for suc in node.sucs: 
      self.get_dominated(suc, dom_nodes) 
      dom_nodes.append(suc) 
######################################################################## 
    def _find_leaves(self, node): 
     """! 
     @brief traverses tree in in order and adds all leaves to leaves field 
       last leaf in list will be right hand corner of tree, due to in 
       order travsersal 
     """ 
     if node.suc_count == 0: 
      self.leaves.append(node) 
     for suc in node.sucs: 
      self._find_leaves(suc) 
######################################################################## 
    class TreeRHCIterator: 
     """! 
     @brief Right hand corner initialised iterator, traverses tree bottom 
       up, right to left 
     """ 
     def __init__(self, tree): 
      self.current = tree.get_leaves(tree.root)[-1] # last leaf is right corner 
      self.csi = len(self.current.sucs)-1 # right most index of sucs 
      self.visited = list() # visisted nodes 
######################################################################## 
     def begin(self, tree): 
      return tree.get_leaves(tree.root)[-1] 
######################################################################## 
     def end(self, tree): 
      return tree.root 
######################################################################## 
     def find_unvisited(self, node, tree): 
      """! 
      @brief finds rightmost unvisited node transitively dominated by node 
      """ 
      leaves = tree.get_leaves(tree.root) 
      # loop through leaves from right to left, as leaves are listed 
      # in order, thus rightmost list elememt is rightmost leaf 
      for i in range(len(leaves)-1, -1, -1): 
       # return the first leaf, that has not been visited yet 
       if leaves[i] not in self.visited: 
        return leaves[i] 
      # return None if all leaves have been visited 
      return None 
######################################################################## 
     def go_up(self, node, tree): 
      """! 
      @brief sets self.current to pred of self.current, 
        appends current node to visited nodes, reassignes csi 
      """ 
      self.visited.append(self.current) 
      self.current = self.current.pred 
      if self.current.sucs[0] not in self.visited: 
       self.current = self.find_unvisited(self.current, tree) 
      self.csi = len(self.current.sucs)-1 
      self.visited.append(self.current) 
######################################################################## 
     def iterate(self, tree): 
      """! 
      @brief advances iterator 
      """ 
      # if current node is a leaf, go to its predecessor 
      if self.current.suc_count == 0 or self.current in self.visited: 
       self.go_up(self.current, tree) 
      # if current node is not a leaf, find the next unvisited 
      else: 
       self.current = self.find_unvisited(self.current, tree) 
######################################################################## 
######################################################################## 

這樣調用:

tree = Tree() 

it = tree.TreeRHCIterator(tree) 
end = it.end(tree) 

while (it.current != end): 
    print it.current.label 
    it.iterate(tree) 

編輯

實現標準迭代器協議,我關於它的工作原理有點糊塗了。不知何故,當循環播放樹時,開始節點會被跳過。所以我做了一個測試課來研究行爲,即使迭代方法基本上以相同的方式工作,也沒有元素被跳過。有人能幫我解釋一下嗎?

重新設計的迭代器:

######################################################################## 
# RIGHT-HAND-CORNER-BOTTOM-UP-POST-ORDER-TRAVERSAL-ITERATOR 
######################################################################## 
    class RBPIter: 
     """! 
     @brief Right hand corner initialised iterator, traverses tree bottom 
        up, right to left 
     """ 
     def __init__(self, tree): 
      self.current = tree.get_leaves(tree.root)[-1] # last leaf is right corner 
      self.csi = len(self.current.sucs)-1 # right most index of sucs 
      self.visited = list() # visisted nodes 
      self.tree = tree 
      self.label = self.current.label 
######################################################################## 
     def __iter__(self): 
      print "iter: ", self.label 
      return self 
######################################################################## 
     def begin(self): 
      return self.tree.get_leaves(self.tree.root)[-1] 
######################################################################## 
     def end(self): 
      return self.tree.root 
######################################################################## 
     def find_unvisited(self, node): 
      """! 
      @brief finds rightmost unvisited node transitively dominated by node 
      """ 
      leaves = self.tree.get_leaves(self.tree.root) 
      # loop through leaves from right to left, as leaves are listed 
      # in order, thus rightmost list elememt is rightmost leaf 
      for i in range(len(leaves)-1, -1, -1): 
       # return the first leaf, that has not been visited yet 
       if leaves[i] not in self.visited: 
        self.label = leaves[i].label 
        return leaves[i] 
      # return None if all leaves have been visited 
      return None 
######################################################################## 
     def go_up(self, node): 
      """! 
      @brief sets self.current to pred of self.current, 
         appends current node to visited nodes, reassignes csi 
      """ 
      self.visited.append(self.current) 
      self.current = self.current.pred 
      if self.current.sucs[0] not in self.visited: 
       self.current = self.find_unvisited(self.current) 
      self.label = self.current.label 
      self.csi = len(self.current.sucs)-1 
      self.visited.append(self.current) 
######################################################################## 
     def next(self): 
      """! 
      @brief advances iterator 
      """ 
      print "next: ", self.label 
      # if current node is a leaf, go to its predecessor 
      if self.current.suc_count == 0 or self.current in self.visited: 
       self.go_up(self.current) 
      # if current node is not a leaf, find the next unvisited 
      else: 
       self.current = self.find_unvisited(self.current) 
      if self.current == self.end(): 
       raise StopIteration 
      return self 
######################################################################## 
######################################################################## 

對於下面的測試文件,我得到了下面的輸出:

tree1 = Tree() 

tree1.add(0, "t") 
tree1.add(1, "e") 
tree1.add(2, "s") 
tree1.add(3, "t") 

tree1.add(2, "t") 
tree1.add(5, "r") 
tree1.add(6, "i") 
tree1.add(7, "s") 

tree1.add(6, "a") 

for node in tree1.RBPIter(tree1): 
    print node.label 

輸出:

iter: a 
next: a 
s 
next: s 
i 
next: i 
r 
next: r 
t 
next: t 
t 
next: t 
s 
next: s 
e 
next: e 
t 
next: t 

樹,這看起來是這樣的:

!因此,正如你所看到的,「a」 - 意味着右手角落節點缺失,我不明白爲什麼,因爲迭代器方法正確返回第一個元素,就像你可以請參閱調試輸出。

+0

你爲什麼不使用普通的迭代器協議:

class PostOrderIter: def __init__(self, tree): self.tree = tree #and some more stuff def __iter__(self): return self class PostOrder: def __init__(self, tree): self.tree = tree #and some more stuff def __iter__(self): return PostOrderIter(self.tree) 

與調用它?實現'__iter__'? – thefourtheye

+0

以前不知道。但是,我也許想爲不同的遍歷命令使用不同的迭代器。 –

+1

對於這個問題,爲什麼不用'self_tree = tree'在'__init__'中存儲'tree'對象? – thefourtheye

回答

2

你可以讓一個迭代器看起來不錯,像這樣的東西:

class TreeIter: 
    def __init__(self, parametersIfAny): 
     code godes here 
    def __iter__(self): 
     return self 
    def __next__(self): 
     code that makes the iteration 

class Tree: 
    def __iter__(self): 
     return TreeIter(parametersIfAny) 

然後你就可以調用它像這樣:

tree = Tree() 

for node in tree: 
    print node.label 

如果你需要有很多不同的迭代器,即序,後期等等。去年我不得不做這樣的事情(雖然有圖表)。我做了什麼,然後是這樣的:

for node in PostOrder(tree): 
    print node.label 
+0

我明白了,謝謝。但我想你不能指定不同的迭代器?例如一個用於遍歷一個用於郵政訂單等?或者如果有人說前進一個和後一個迭代? 編輯:通過編輯得到了ninjaed –