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
Related
My attempts at creating a linked list from scratch in Python are not working, and I'm not sure what I am missing. I tried to create separate classes for nodes and linked lists, but when I am trying to see beyond the head node, I hit a road block.
Appreciate any pointers (no pun intended). Also, what is a better approach: have a separate class for the nodes, or make them an attribute of the linked list class itself?
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class LinkedList:
def __init__(self):
self.head = None
# adding/inserting to the tail
def add(self, val):
node_to_add = ListNode()
if self.head == None:
self.head = node_to_add
node_to_add.val = val
else:
self.next = node_to_add
node_to_add.val = val
# printing the linked list as a list
def print(self):
list_to_print = []
if not self.head:
return None
node_to_read = self.head
while self.head:
list_to_print.append(node_to_read.val)
if node_to_read.next:
self.head = node_to_read.next
else:
return list_to_print
When I run this code, I only can print the head node. When I add nodes/values after the first entry, the print() only returns the head.
test1 = LinkedList()
test1.add(1)
test1.add(4)
test1.add(7)
test1.print()
the output is
[1]
In order to append items to the last Node you need to keep track of the last node.
You also should not override your head pointer in the print function.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
# adding/inserting to the tail
def add(self, val):
node_to_add = ListNode()
node_to_add.val = val
if self.tail == None:
self.head = node_to_add
self.tail = node_to_add
else:
self.tail.next = node_to_add
self.tail = node_to_add
# printing the linked list as a list
def print(self):
list_to_print = []
if not self.head:
return None
current_node = self.head
while current_node:
list_to_print.append(current_node.val)
if current_node.next:
current_node = current_node.next
else:
return list_to_print
You can simplify this significantly by giving the node responsibility for adding children. This makes the linked list object basically a wrapper that manages the head and iterations over the list:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def add(self, node):
if self.next is None:
self.next = node
else:
self.next.add(node)
class LinkedList:
def __init__(self):
self.head = None
def add(self, val):
node = ListNode(val)
if self.head == None:
self.head = node
else:
self.head.add(node)
# printing the linked list as a list
def print(self):
list_to_print = []
node_to_read = self.head
while node_to_read:
list_to_print.append(node_to_read.val)
node_to_read = node_to_read.next
print(list_to_print)
test1 = LinkedList()
test1.add(1)
test1.add(4)
test1.add(7)
test1.print()
# [1, 4, 7]
So i started learning about listNodes or linked lists in python and created one;
class Node:
def __init__(self, data, next = None):
self.data = data
self.next = next
class LinkedNode:
def __init__(self):
self.head = None
self.size = 0
def add(self, data):
self.head = Node(data, self.head)
self.size += 1
It works fine but how can i get a listNode in python as the result of this node.
What i mean is a list node looks like this;
{val: 'some_val', next: {val: 'other_val', next{...}}}
In js, when we print the instance of the list node class, it gets the result in the same format but, in python when i tried the same;
ln = LinkedNode()
print(ln)
It gives this;
<main.LinkedNode object at 0x7fe191576400>
There are several ways to make your instance print something more useful.
For instance, you could add a method that will produce a dict, because dictionaries are printed in an output format that is similar to what you ask for:
class Node:
def __init__(self, data, next = None):
self.data = data
self.next = next
def asdict(self):
return { "data": self.data, "next": self.next and self.next.asdict() }
So now when you do this:
print(Node(1).asdict())
... you'll get:
{'data': 1, 'next': None}
When you also add such a method to the LinkedNode class:
class LinkedNode:
def __init__(self):
self.head = None
self.size = 0
def add(self, data):
self.head = Node(data, self.head)
self.size += 1
def asdict(self):
return self.head and self.head.asdict()
...you can do:
lst = LinkedNode()
lst.add(1)
lst.add(2)
print(lst.asdict())
And that will output:
{'data': 2, 'next': {'data': 1, 'next': None}}
And finally, if you want this output to be the default that print will use when you just do print(lst), then define __repr__ (or __str__) on the class:
class LinkedNode:
def __init__(self):
self.head = None
self.size = 0
def add(self, data):
self.head = Node(data, self.head)
self.size += 1
def asdict(self):
return self.head and self.head.asdict()
def __repr__(self):
return repr(self.asdict())
Now you can do print(lst) without calling .asdict() explicitly, and get that same output.
The "append" method is not working correctly.
It's only going inside the 'if" statement of the "append' method and not entering into the while loop.
class Node:
def __init__(self,data=None):
self.data=data
self.next=None
class Linkedlist:
def __init__(self):
self.head=Node()
def append(self,data):
new_node=Node(data)
if self.head.data is None:
self.head=new_node
cur_node=self.head
while cur_node.next is not None:
cur_node=cur_node.next
cur_node=new_node
def insert_after_node(self,prev_node,data):
new_node=Node(data)
if prev_node is None:
print("node that you have entered does not exist")
new_node.next=prev_node.next
prev_node.next=new_node
def display(self):
current=self.head
while current.next is not None:
print(current.data)
current=current.next
List=Linkedlist()
List.append("A")
List.append("B")
List.append("C")
List.insert_after_node(List.head,"g")
List.display()
Expected output: AgBC
Actual Output: A
Your .append() method simply sets a local variable to cur_node to point to new_node. This doesn't change the linked list at all; the last node in the link that was previously assigned to that local variable is not changed.
You instead want to assign to the .next attribute of the last node:
cur_node.next = new_node
The while loop in the method is working fine otherwise.
You also should not use new_node twice, when the list is empty. Exit when you don't yet have a head node with data:
if self.head.data is None:
self.head=new_node
return
Personally, I'd set self.head = None instead of self.head = Node(), then use if self.head is None:.
Next, your display function forgets to print the last element. Rather than test for current.next, check if current is None; this is where setting self.head to None for an empty list would work a lot better:
def display(self):
current = self.head
while current is not None
print(current.data)
current = current.next
I had the exact same question as you, but my implementation was different and it seems to work just fine.
First I created a node class with all its different methods:
class Node:
def __init__(self, init_data):
self.data = init_data
self.next = None
def get_data(self):
return self.data
def get_next(self):
return self.next
def set_data(self, new_data):
self.data = new_data
def set_next(self, new_next):
self.next= new_next
The I created my UnorderedList class with its methods too, including append. insert, index and pop I am still working on...
class UnorderedList:
"""
An unordered list class built from a collection of nodes.
"""
def __init__(self):
self.head = None
def is_empty(self):
return self.head == None
def add(self, item):
temp = Node(item)
temp.set_next(self.head)
self.head = temp
def size(self):
current = self.head
count = 0
while current != None:
count += 1
current = current.get_next()
return count
def search(self, item):
current = self.head
found = False
while current != None and not found:
if current.get_data() == item:
found = True
else:
current = current.get_next()
return found
def remove(self, item):
current = self.head
previous = None
found = False
while not found:
if current.get_data() == item:
found = True
else:
previous = current
current = current.get_next()
if previous == None:
self.head = current.get_next()
else:
previous.set_next(current.get_next())
def print_list(self):
current = self.head
while current != None:
print(current.data)
current = current.get_next()
def append(self, item):
new_node = Node(item)
if self.head == None:
self.head = new_node
return
current = self.head
found_last = False
while not found_last:
if current.get_next() == None:
found_last = True
current.set_next(new_node)
else:
current = current.get_next()
def insert(self, item, pos):
pass
def index(self, item):
pass
def pop(self):
pass
I realize my version of append is more verbose, but I was using the traversal method I previously used as part of my size method to make it work, and it seems to work just fine.
I found it easier to create a Node class with its methods separately as it made it easier to visualize how to set the next node and get data from the current one. Hope this helps!
FYI the node class and a lot of the UnorderedList is straight out of Problem Solving with Algorithms and Data Structures in Python by David Ranum and Brad Miller, so I know they work fine!
class Node:
def __init__(self,data=None):
self.data=data
self.next=None
class LinkedList():
def __init__(self):
self.head=Node()
def append(self,data):
new_node=Node(data)
if self.head.data is None:
self.head=new_node
else:
cur_node=self.head
while cur_node.next is not None:
cur_node=cur_node.next
cur_node.next=new_node
def insert_after_node(self,prev_node,data):
new_node=Node(data)
if prev_node is None:
print("node that you have entered does not exist")
new_node.next=prev_node.next
prev_node.next=new_node
def display(self):
current=self.head
while current.next is not None:
print(current.data)
current=current.next
print(current.data)
List=LinkedList()
List.append("A")
List.append("B")
List.append("C")
List.insert_after_node(List.head,"g")
Fixed some bugs for you:
The class name LinkedList mismatch problem
In append, if self.head.data is None, it should set self.head and then return.
In the else part of append, let the last node point to the new node by cur_node.next=new_node
In display, the last node should also be printed.
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
I'm writing an implementation of doubly linked lists. In order to traverse the list, I'm using something like:
class Node:
""" A node in our linked list """
def __init__(self, value: Any, next: Union['Node', None] =None,
previous: Union['Node', None] =None) -> None:
self.value = value
self.next = next
self.previous = previous
...
def __next__(self, direction: int =1) -> Union['Node', None]:
if direction == 1:
return self.get_next()
else:
return self.get_previous()
...
where get_next and get_previous are just getters of self.next and self.previous.
However, PyCharm yells at me for trying to call next as
next(some_node, direction=-1). What's the proper way to do this?
Besides __iter__ there is also __reversed__. Both are required to return iterators. The __next__ method should be implemented on iterators (not on node-classes). Note that all magic methods (when called by a function like next instead of directly invoked) need to implement the expected arguments not more - not less.
For example a doubly linked list could just implement __iter__ and __reversed__ and rely on next and previous attribute of the Node:
class Node(object):
def __init__(self, val, nxt, prv):
self.val = val
self.nxt = nxt
self.prv = prv
class DoublyLinkedList(object):
def __init__(self, base=None, last=None):
self.base = base
self.last = last
def prepend(self, val):
new = Node(val, self.base, None)
if self.base is None:
self.base = new
self.last = new
else:
self.base.prv = new
self.base = new
def append(self, val):
new = Node(val, None, self.last)
if self.last is None:
self.base = new
self.last = new
else:
self.last.nxt = new
self.last = new
def __iter__(self):
current = self.base
while current is not None:
yield current
current = current.nxt
def __reversed__(self):
current = self.last
while current is not None:
yield current
current = current.prv
For example:
dl = DoublyLinkedList()
dl.prepend(10)
dl.prepend(20)
dl.prepend(30)
for i in dl:
print(i.val)
gives:
30
20
10
similar for reversed:
for i in reversed(dl):
print(i.val)
# prints:
10
20
30
__next__ is part of the iterator protocol and should be used as described in said protocol, doing otherwise only make problems with the rest python.
In your case just rename the function to simple next and use as some_node.next(-1), though I would change the direction argument to a boolean, as that is how you use it, and its name too. Like this for example
class None:
...
def next(self, forward:bool=True) -> Union['Node', None]:
if forward:
return self.get_next()
else:
return self.get_previous()
and use as some_node.next(), some_node.next(False) or even some_node.next(0) (using 0 instead of False for the same effect)
The extra argument to next is a default value, and __next__ doesn't take any extra arguments. Python doesn't have any sort of two-way iterators. If your interface is not exactly the same as for i in obj:, then you should write your own.