Suppose you have an inner class
class SomeTransformer(ast.NodeTransformer):
def visit_Global(self, node):
*transform AST*
super().generic_visit(node)
return node
def visit_Assign(self, node):
*transform AST*
super().generic_visit(node)
return node
def visit_FunctionDef(self, node):
*transform AST*
super().generic_visit(node)
return node
Would it be more effective to override generic_visit() in a way below, because in the first version the whole tree will be traversed completely three times, looking for nodes of specific type only, while in the second version it will be transformed in a single traversal?
class SomeTransformer(ast.NodeTransformer):
def generic_visit(self, node):
for child in ast.iter_child_nodes(node):
if isinstance(child, ast.Global):
*transform AST*
if isinstance(child, ast.Assign):
*transform AST*
if isinstance(child, ast.FunctionDef):
*transform AST*
return node
For example if I have 2 classes Tree and Node, in the add_node method in Tree, I can add self referring to the Tree into the Node object as shown below. This is done for convenience when accessing other parts of the Tree with just the keys once in the Node object (i.e. can just use keys). However, I was wondering if this was considered best practice since the IDE (PyCharm) also screams Unresolved attribute reference 'tree' for class 'Node' so might be not a great sign as well.
class Tree:
def __init__(self):
self.nodes = {}
def add_node(self, count):
new_node = Node()
new_node.tree = self
self.nodes[count] = new_node
class Node:
def __init__(self):
# Some data e.g. parent, children index
pass
Pass the tree in the initializer for the Node
class Node:
def __init__(self, tree):
self.tree = tree
And when creating the node:
...
new_node = Node(self)
self.nodes[count] = new_node
...
Inside a recursive class function, how do i use a class member as default argument?
I am currently using following snippet that works (python 3). height is recursive class function. Default argument that i want to use for node is self._root. In order to achieve this, i do something like following but thats a very round about way where i have to define another function height_tree. Is there a more direct approach?
# following code works #
class BTree:
def __init__(self, node=None):
self._root = node
def height_tree(self):
return self.height(self._root)
def height(self, node):
if node is not None:
height = max(self.height(node.get_left()), self.height(node.get_right())) + 1
else:
height = 0
return height
I want to do something like following but it obviously doesn't works !
def height(self, node=self._root)
# code from above follows
I however cannot use alternate trick of assigning root to node when node is None since that is one of the end condition in recursion
def height(self, node)
if node is None:
node = self._root
# this will conflict with logic in code block above#
Are there any recommended ways to handle such a situation?
You can use a sentinel object:
sentinel = object()
def height(self, node=sentinel)
if node is sentinel:
node = self._root
...
Since such object would have a unique memory address, it would never be identical to any other object (including None).
You can always create arbitrary sentinels:
SENTINEL = object()
class BTree:
def __init__(self, node=None):
self._root = node
def height_tree(self):
return self.height(self._root)
def height(self, node=SENTINEL):
if node is SENTINEL:
node = self._root
elif node is not None:
height = max(self.height(node.get_left()), self.height(node.get_right())) + 1
else:
height = 0
return height
I have a a binary tree, with only self._root and self._nodes. Is there anyway I could figure out how to find a parent tree of the current tree through the constructor?
class BinaryTree:
"""
- If _root is None, then _nodes is empty and _parent_tree is None
- nodes is allowed to contain empty nodes
- if _parent_tree is not empty, then self is in _parent_tree._nodes
"""
def __init__(self, root, nodes):
"""Initialize a new BinaryTree.
#type self: BinaryTree
#type root: object
#type nodes: list[BinaryTree]
#rtype: None
"""
self._root = root
self._nodes = nodes
self._parent_tree = None
"""Implement _parent_tree"""
if self._root is not None:
"""implement"""
else:
pass
To be honest, I have no idea of how to even start this. How would I know any other BinaryTree objects without constructing this one first. What would I be recursively looking through? I can't possibly look through this BinaryTree
Following should do good for you :
class BinaryTree:
def __init__(self, root, nodes):
self._root = root
self._nodes = nodes
self._parent_tree = None
if self._root is not None:
for node in self._nodes:
if node is not None:
node._parent_tree = self
else:
pass
While writing an iterator for a tree class in python I stumbled upon the issue, that I apparently cannot access fields and methods of the mother class without instanciating an iterator instance with a reference to an already existing Tree instance, so I always need to call the iterator like "it.iterate(tree)" which is pretty ugly. I was wondering whether there was some way to design this stuff, so an instance reference for the iterator and its methods is not required. SO, can I somehow access the fields of an instance of Tree from an instance of the Iterator without passing a reference of the Tree instance to the iterator?
########################################################################
# 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)
########################################################################
########################################################################
called like this:
tree = Tree()
it = tree.TreeRHCIterator(tree)
end = it.end(tree)
while (it.current != end):
print it.current.label
it.iterate(tree)
Edit
After implementing the standard iterator protocol I am a little confused about its workings. Somehow the start node is being skipped when looping over the tree. SO I made a test class to study the behavior, and there no element is being skipped, even though the iteration method basically works the same way. Could somebody shed some light on this for me?
Redesigned iterator:
########################################################################
# 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
########################################################################
########################################################################
For the following test file I get the the following output:
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
output:
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
The tree to this looks like this:
! 1 [a trie]
So, as you see, the "a" - meaning the right hand corner node is missing, and I don't understand why, because the iterator method returns the first element correctly, as you can see in the debug output.
You can make an iterator look nice with something like this:
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)
Then you can invoke it like this:
tree = Tree()
for node in tree:
print node.label
If you need to have many different iterators, i.e. inorder, postorder, etc. I had to do something like this last year (albeit with graphs). What i did then was something like:
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)
to invoke it with:
for node in PostOrder(tree):
print node.label