Python class iterator - python

I have a class of the node which contain his parent and want to create iterator on it. Here is my try:
class Node:
def __init__(self, parent=None):
self._parent = parent
def __iter__(self):
self = self.parent
def __next__(self):
if self.parent is None:
raise StopIteration
else:
self = self.parent
return self
But when I try to loop over the instance, it's never stops and returns the same value, what I did wrong?

The reason your code doesn't work is that you're trying to keep track of the current node in the iterator by assigning to self, which is just a local variable, so nothing is actually updated.
The correct way would be to extract an iterator class and keep track of the current node there:
class Node:
def __init__(self, parent=None):
self.parent = parent
def __iter__(self):
return NodeIterator(self)
class NodeIterator:
def __init__(self, node):
self.next_node = node
def __iter__(self):
return self
def __next__(self):
if self.next_node is None:
raise StopIteration
else:
current_node = self.next_node
self.next_node = self.next_node.parent
return current_node
This can be used like so:
root = Node()
inner_1 = Node(root)
leaf_1 = Node(inner_1)
inner_2 = Node(root)
inner_2_1 = Node(inner_2)
leaf_2 = Node(inner_2_1)
for node in leaf_2:
# will loop through:
# leaf_2,
# inner_2_1;
# inner_2,
# root

Related

Anytree NodeMixin

I want to use the NodeMixin Module, but it's not really clear to me if I read the documentation how to do that.
https://anytree.readthedocs.io/en/2.8.0/api/anytree.node.html#anytree.node.nodemixin.NodeMixin
I have a BaseClas like this:
from anytree import NodeMixin, RenderTree
class Tree():
def __init__(self, data, parent):
self.data = data
self.parent = parent
self.children = []
def __eq__(self, other):
if isinstance(other, Tree):
return self.data == other.data
else:
return False
def __repr__(self):
return "Tree("+str(self.data)+","+str(self.children)+")"
def __str__(self):
return self.__repr__()
def update_parent(self, new):
self.parent = new
def add_child(self, c):
self.children.append(c)
def rm_child(self, c):
self.children.remove(c)
If I want to use the NodeMixin Module I would need something like this:
class TreeMix(Tree, NodeMixin): # Add Node feature
def __init__(self, name, length, width, parent=None, children=None):
super(TreeMix, self).__init__()
self.name = name
self.length = length
self.width = width
self.parent = parent
if children:
self.children = children
I get this Error Message:
init() missing 2 required positional arguments: 'data' and 'parent'
I create Objects like this:
my0 = TreeMix('0', 0, 0, parent=None)
my1 = TreeMix('1', 1, 0, parent=my0)
my2 = TreeMix('2', 2, 0, parent=my1)
Did I get the Constructor wrong?

Change object attribute in class method without explicitly naming the attribute

I have a class Node that I built using python parent child relationship class. This class defines the parent/children relationship needed to build a tree. In my application, I am building a balance sheet.
class Node:
_parent = None
def __init__(self, name='', attributes=None, children=None, parent=None):
self.name = name
self.children = children if children is not None else []
self.parent = parent
if children is not None:
for child in children:
child.parent = self
#property
def parent(self):
return self._parent() if self._parent is not None else None
#parent.setter
def parent(self, newparent):
oldparent = self.parent
if newparent is oldparent:
return
if oldparent is not None:
oldparent.children.remove(self)
if self not in newparent.children:
newparent.children.append(self)
self._parent = weakref.ref(newparent) if newparent is not None else None
I also define a subclass LineItem that I use to populate the nodes of my tree. When a LineItem object with a given attribute (e.g. balance) gets added to the tree, I would like that attribute to be added and rolled up the tree. For example, if node 1 has two children node 2 and 3 with each a balance of 10 and 20 respectively, then Node 1 would get a balance of 30.
class LineItem(Node):
def __init__(self, enabled=True, balance=None, native_curr=None, reported_curr='USD', *args, **kwargs):
super().__init__(*args, **kwargs)
self.enabled = enabled
self.balance = balance
self.currency = native_curr
self.report_curr = reported_curr
# propagate the balance up the balance sheet
super().addrollup(self.balance)
I created a method in the class Node that works well if the attribute is always balance.
# propagate the quantity up
def addrollup(self, quantity):
curr_parent = self.parent
if quantity is not None:
while curr_parent is not None:
if curr_parent.balance is not None:
curr_parent.balance += quantity
else:
curr_parent.balance = quantity
curr_parent = curr_parent.parent
How would I write this function if I didn't want to explicitly call out "balance"? Is it possible to write a function generic enough that it takes an argument to define what attribute should be rolled up?
Thank you for your comment Željko Jelić. After looking at setattr and getattr, I realized that they could be used to pass the name as an argument instead of using the self.attribute = syntax.
I rewrote the function as
def addrollup(self, attr_name, value):
curr_parent = self.parent
if value is not None:
while curr_parent is not None:
if getattr(curr_parent, attr_name) is not None:
setattr(curr_parent, attr_name, getattr(curr_parent, attr_name)+value)
else:
setattr(curr_parent, attr_name, value)
curr_parent = curr_parent.parent

Creating python objects of same type from nested class

My List class has a nested _Node class within it. With these 2 classes, I can:
Initialize an object of type List within List.
Initialize an object of type _Node within List.
Initialize an object of type List within _Node.
I can't, however, initialize a _Node within a _Node. The last line of the code is below causes NameError: global name '_Node' is not defined
class List:
def __init__(self):
self.root = None
self.brother = None
def set_root(self):
self.root = self._Node()
def set_brother(self):
self.brother = List()
def set_root_next(self):
self.root.set_next()
class _Node:
def __init__(self, next_node=None, data=None):
self.next_node = next_node
self.data = data
def set_next(self):
self.next_node = _Node()
if __name__ == '__main__':
x = List()
x.set_root()
x.set_brother()
x.set_root_next()
How do I solve this? Making the _Node class unnested works but I am planning to have many types of list in the same file. All accompanying nodes will inherit from a single abstract class so keeping this structure is important.
Try using self.__class__() instead
class List:
def __init__(self):
self.root = None
self.brother = None
def set_root(self):
self.root = self._Node()
def set_brother(self):
self.brother = List()
def set_root_next(self):
self.root.set_next()
class _Node:
def __init__(self, next_node=None, data=None):
self.next_node = next_node
self.data = data
def set_next(self):
self.next_node = self.__class__()
if __name__ == '__main__':
x = List()
x.set_root()
x.set_brother()
x.set_root_next()

Python Linked List with Nodes. Iterable

I need some help writing an __iter__() method for my UnorderedList() class. I tried this:
def __iter__(self):
current = self
while current != None:
yield current
But the while loop doesn't stop. Here is the rest of my classes and code:
class Node:
def __init__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self,newdata):
self.data = newdata
def setNext(self,newnext):
self.next = newnext
class UnorderedList:
def __init__(self):
self.head = None
self.count = 0
If you want to iterate all items succeedingly, you should do
def __iter__(self):
# Remember, self is our UnorderedList.
# In order to get to the first Node, we must do
current = self.head
# and then, until we have reached the end:
while current is not None:
yield current
# in order to get from one Node to the next one:
current = current.next
so that in every step you go one step further.
BTW, setters and getters aren't used in Python in the form of methods. If you need them, use properties, otherwise omit them altogether.
So just do
class Node(object):
def __init__(self, initdata):
self.data = initdata
self.next = None
class UnorderedList(object):
def __init__(self):
self.head = None
self.count = 0
def __iter__(self):
current = self.head
while current is not None:
yield current
current = current.next

Finding a Node in a Tree using recursion in python

class Node:
def __init__(self, tree, data, parent=None):
self.data = data
self.parent = parent
self.children = []
self.tree = tree
def find(self, x):
if self.data is x:
return self
elif self.children:
for node in self.children:
return node.find(person)
else:
return None
I am really stuck, i can't seem to create a method in my Node class that finds a Node with data x and returns that Node. If no Node is found, it will return None.
you're searching for parent in children, while you should search for x
class Node():
def __init__(self, tree, data, parent=None):
self.data = data
self.parent = parent
self.children = []
self.tree = tree
def find(self, x):
if self.data is x: return self
for node in self.children:
n = node.find(x)
if n: return n
return None
>>> n = Node(None, 1)
>>> n.children = [Node(None, 2), Node(None, 3)]
>>> print n.find(3).data
3
I think the immediate fix would be to change this
for node in self.children:
return node.find(person)
To this
for node in self.children:
res = node.find(person)
if res is not None:
return res

Categories