My self-defined queue not printing properly - python

I'm currently doing a class assignment to create a Class Queue using linked list. My code is as follows:
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class Queue:
def __init__(self):
self.front = None
self.rear = None
def enqueue(self, value):
newNode = Node(value)
if self.rear is None:
self.front = newNode
self.rear = self.front
else:
self.rear.next = newNode
self.rear.next.prev = self.rear
self.rear = self.rear.next
def dequeue(self):
if self.front is None:
return None
else:
to_remove = self.front.data
self.front = self.front.next
self.front.prev = None
return to_remove
def printQueue(self):
print('The elements in the queue are ')
element = self.front
while element is not None:
print(element, end = "")
element = element.next
myqueue = Queue()
for i in range(21):
if i % 3 == 0:
myqueue.enqueue(i)
elif i % 5 == 0:
myqueue.dequeue()
myqueue.printQueue()
But when I tried printing, it comes up like this:
<__main__.Node object at 0x000001EB465D8048><__main__.Node object at 0x000001EB465D8128><__main__.Node object at 0x000001EB465D8080><__main__.Node object at 0x000001EB465D80F0>
I searched on the internet and tried to change my code but it still shows up the same. I don't understand why though.

As the comment says, and I modified your questions itself, too. You have to print the value of the objects, not the objects themselves. It's not an error, just a little confusion on your part.

When you print out element you print the location of the object's memory.
<__main__.Node object at 0x000001EB465D8048><__main__.Node object at 0x000001EB465D8128><__main__.Node object at 0x000001EB465D8080><__main__.Node object at 0x000001EB465D80F0>
The hexadecimal values are addresses.
In order to access the value present at these locations use element.data instead of element.

First of all, that is not an error. Your printQueue function iterates the elements and prints each element. The type of each element is Node.
For example, your output contains the representation of the nodes:
The object below is a single node, defined in the __main__ function. Its type is Node Object and its memory address is 0x000001EB465D8048
<__main__.Node object at 0x000001EB465D8048>
To print a user friendly version of each node, the node class needs to override the __str__ method.
For example, if you would modify your Node class to have the following structure, then you could just use print(element, end = "\n") inside your printQueue method.
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
def __str__(self):
return str(self.data)
The output then would be
The elements in the queue are
9
12
15
18
Furthermore, while not directly related to your question, it's tangential enough that you might find it helpful. Python objects have both a __str__ and a __repr__method.
Simply put, the __str__ is used to represent the readable form of your object, and the __repr__ the unambiguous representation.
For more information on those 2 methods, this stackoverflow post does a good job at explaining them.

Related

Trying to insert and print the data in linked list, got an error 'int' object has no attribute 'data'

creating a linked list
inserting new data in linked list at end
printing linked list in list
Expected output: [5,6,2,8]
class node():
def __init__(self, data):
self.data = data
self.next = None
class linkedlist():
def add_at_end(self,newval,head):
newnode = node(newval)
while head.next:
head = head.next
head.next = newval
def print_list(self,head):
LL = []
while head:
LL.append(head.data)
head = head.next
return LL
x = node(5)
y = node(6)
z = node(2)
x.next = y
y.next = z
vals = linkedlist()
print(vals.add_at_end(8,x))
print(vals.print_list(x))
The error occurs because you have head.next = newval where newval is a number, but you should be assigning a node instance, i.e. newnode. So the next time you iterate the list, like in print_list you'll bump into that integer, and access its data attribute, as if it is a node...
Some other remarks on your code:
Your linkedlist class has no state. It merely provides some functions that act on the head argument. The OOP way to do this, is to give a linkedlist instance its own head attribute. And then rely on the self argument, which in your current code is not used in the functions.
Your main code should not have to deal with node instances. It should just have to create a linkedlist, and then populate it by calling its method(s). For that to work you'll have to deal with the case where the list is empty, and a first value is added to it, because then its head attribute needs to be set to it.
Use PascalCase for naming your classes.
The name of the print_list function is misleading: it doesn't print, but returns a list. So I would name it to_list instead.
Don't print the result of calling vals.add_at_end, because it is not designed to return anything, so you'll be printing None.
Corrected:
class Node(): # Use PascalCase
def __init__(self, data):
self.data = data
self.next = None
class LinkedList():
def __init__(self):
self.head = None # Give each instance its own head attribute
def add_at_end(self, newval):
if not self.head: # Deal with this case
self.head = Node(newval)
else:
head = self.head # Use attribute
while head.next:
head = head.next
head.next = Node(newval) # corrected
def to_list(self): # Better name
LL = []
head = self.head # Use attribute
while head:
LL.append(head.data)
head = head.next
return LL
vals = LinkedList()
vals.add_at_end(5) # Use method to add nodes
vals.add_at_end(6)
vals.add_at_end(2)
vals.add_at_end(8) # Nothing to print here
print(vals.to_list())

Why creating an instance of a class within a class method changes the 'self' argument?

I'm writing a linked list in Python and I've come across an issue which is really troublesome and terrible for debugging and I feel like I'm missing something about Python. I'm supposed to create a singly linked list with some basic functionalities. One of them is the take() function, which is meant to create a new list of n first elements of the original list.
However, creating a new instance of the LinkedList class seems to change the .self parameter and the variable node is modified, as the attribute .next is turned to None. In result, when creating a list and then trying to make a new one out of the n elements of it, the program runs indefinitely, but no matter which part I look at, I cannot find the loop or the reason behind it.
class LinkedList:
def __init__(self, head=None):
self.head = head
def is_empty(self):
if self.head == None:
return True
else:
return False
def add_last(self, node):
if self.is_empty():
self.head = node
return
nextEl = self.head
while True:
if nextEl.next is None:
nextEl.next = node
return
nextEl = nextEl.next
def take(self, n):
node = self.head
newHead = self.head
newHead.next = None
newList = LinkedList(newHead)
count = 0
while count < n:
newList.add_last(node.next)
node = node.next
count += 1
return newList
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
Thank you for all your help.
In the take() function the line
newHead.next = None
modifies a node of the linked list, breaking this list. You can fix this as follows:
def take(self, n):
node = self.head
newHead = Node(self.head.data)
newList = LinkedList(newHead)
count = 0
while count < n:
newList.add_last(node.next)
node = node.next
count += 1
return newList
This, however, still will not work correctly since there is a problem with the add_last() function too. This function, I think, is supposed to add a node as the last element of a linked list, but since you do not modify the next attribute of the node, you actually append a whole linked list starting with that node. This can be fixed in the following way:
def add_last(self, node):
if self.is_empty():
self.head = node
return
nextEl = self.head
while True:
if nextEl.next is None:
nextEl.next = Node(node.data)
return
nextEl = nextEl.next
There are more issues. For example, take(sefl, n) will actually create a list of n+1 elements and will throw an exception if it is applied to a linked list that does not have that many elements.

Not understanding some composition aspects (not composition)

I am brushing up on some data structures and algorithms with Python, so I am implementing an Unordered Linked list. Within the same file I first wrote a Node class followed by a List class. What I don't get is how the "current" variable in my search_item() method seems to be a node object or at least able to access the Node class methods and attributes. I noticed that if I comment out my add_node() method then "current" no longer has access to Node's methods. Now I am not explicitly using neither inheritance nor composition, so I am having a hard time seeing how current just gets to call get_next() the way the code is written below. I would think I'd have to declare current as: current = Node(self.head) but just current = self.head seems to work?
Your help would be greatly appreciated.
class Node:
def __init__(self, data):
self.data = data
self.next = None
def get_data(self):
return self.data
def set_data(self, d):
self.data = d
def get_next(self):
return self .next
def set_next(self, n):
self.next = n
class UnorderedList:
def __init__(self):
self.head = None
def add_node(self, item):
tmp = Node(item)
tmp.set_next(self.head)
self.head = tmp
def search_item(self, item):
current = self.head
# current = Node(self.head)
found = False
while current != None and not found:
if current.get_data() == item:
found = True
else:
current = current.get_next()
return found
Well if you comment out add_node then you do not add nodes to your linked list any longer therefore search_item will always see the initial value of self.head which is None.
Calling current.get_next() just works because via add_node you always ensure that self.head points either to None or to an instance of Node as tmp is created by tmp = Node(item) and then assigned to self.head = tmp. Therefore when setting current = self.head it will already refer to an instance of Node (or None) so you do not need to call current = Node(self.head).
I just recently came across the concept of duck typing and this old post of mine came to mind. It seems that this is what's at play and just didn't understand it at the time. 'current' is set to None by default, by when invoking any of the methods defined in the Node class, it is automatically defined as Node object.

Need help understanding python simple linked list program

Below is a simple linked list program, I know how a linked list works conceptually ( adding, removing, etc) but I am finding it hard to understand how it works from an object oriented design perspective.
Code:
class Node():
def __init__(self,d,n=None):
self.data = d
self.next_node = n
def get_next(self):
return self.next_node
def set_next(self,n):
self.next_node = n
def get_data(self):
return self.data
def set_data(self,d):
self.data = d
class LinkedList():
def __init__(self,r = None):
self.root = r
self.size = 0
def get_size(self):
return self.size
def add(self,d):
new_node = Node(d,self.root)
self.root = new_node
self.size += 1
def get_list(self):
new_pointer = self.root
while new_pointer:
print new_pointer.get_data()
new_pointer = new_pointer.get_next()
def remove(self,d):
this_node = self.root
prev_node = None
while this_node:
if this_node.get_data() == d:
if prev_node:
prev_node.set_next(this_node.get_next())
else:
self.root = this_node
self.size -= 1
return True
else:
prev_node = this_node
this_node = this_node.get_next()
return False
def find(self,d):
this_node = self.root
while this_node:
if this_node.get_data() == d:
return d
else:
this_node = this_node.get_next()
return None
myList = LinkedList()
myList.add(5)
myList.add(8)
myList.add(12)
myList.get_list()
I have couple questions here..
How is it storing the values. As far as I understand each variable can hold one value. So how does data / next_node hold multiple values. And does next_node hold the memory location of the next node?
new_pointer.get_data() How is new_pointer able to access get_data()? Don't we need to have an instance to access methods of Node?
This question may be silly, but I am quiet new to object oriented programming. If someone can answer these questions or post an external link addressing these questions it would be really helpful.
Thanks in advance.
next_node is an instance of Node and so it has its own data field. next_node is a reference to the node object, which is some memory address (however it is not a C-like pointer, as you don't need to dereference it or anything).
I'm assuming you are talking about get_list(). new_pointer is an instance of Node. (unless it is None, in which case you would never get into the get_data() call). When you do an add, you create this instance of Node and set root to it. Then in get_list you set new_pointer to root.
myList.root is storing one value only that is the root of the list. See initially when you do:
myList = LinkedList()
in memory myList.root = None (according to __init__ of LinkedList). Now:
myList.add(1)
Then this statement is called:
new_node = Node(d,self.root) #Note here self.root = None
and then:
def init(self,d,n=None):
self.data = d
self.next_node = n
So our list is : 1--> None.Now:
myList.add(2)
then again this statement is called:
new_node = Node(d,self.root) #Note here self.root has some value
now a new node object is created and its next is assigned to myList.root.
So our list becomes : 2-->1--> None
Going in similar fashion whole list is assigned.
Key thing to note here is that myList.root is always storing the top most node which in turn holds the next node and so on.
For your second question, it is quite clear from above explaination that always the object of class node is available to myList.root which in turn has next_node which is again an object of 'node'. So they all have access to 'get_data()' method.

Behavior of del operator in Python

I am wondering why the following doesn't work.
class Node(object):
def __init__(self, data, next=None):
self.data = data
self.next = next
def remove(self, value):
if self is None:
return False
if self.data == value:
if self.next:
self.data = self.next.data
self.next = self.next.next
else:
del self
else:
self.next.remove(value)
node = Node(4)
node.append(Node(3))
node.remove(3)
print node.next.data
#prints 3
del doesn't delete the element from the linked list. I had to modify the delete() function so that I have a pointer to the parent of the target element.
class Node(object):
def __init__(self, data, next=None):
self.data = data
self.next = next
def remove(self, value):
if self is None:
return False
if self.data == value:
if self.next:
self.data = self.next.data
self.next = self.next.next
else:
del self
else:
current = self
while current.next:
if current.next.data == value:
if current.next.next:
current.next = current.next.next
else:
current.next = None
From the console,
node = Node(4)
current = node
del current #node is not deleted because I am only deleting the pointer
del node #node is deleted
This seems logical to me. However, I am not sure why the first code block doesn't work as I expect.
I ll explain why it doesn't work. But first you need to know that you rarely need to create linked lists in Python since the list type already offer you almost everything.
>>> [2*i for i in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> list_1=[2*i for i in range(10)]
>>> i=0
>>> list_1[i]#first
0
>>> i=i+1
>>> list_1[i]#next
2
>>> list_1[-1]#last
18
>>> len(list_1)#size
10
>>> del list_1[-1]# del the last
#delete all (list_1 becomes empty)
for i in range(len(list_1)):
del list_1[0]
at the end loop we only delete the first n times but when you del the first in a list the other object go 1 place back.That way you can easily manipulate lists as if they were linked list and delete any elements without worrying about an empty slot. In addition to this, list have several very usefull methods like append, remove, sort, shuffle and more. Look at the docs https://docs.python.org/3.5/library/stdtypes.html?highlight=list#list
Now back to you question:
Let s take a look to your remove method
if self is None:
return False
this is useless, you can only call .remove with an object that has this method.
None.remove() never works. If self is None it will throw an error before any (impossible) call.
else:
del self
When you see self in an Object's method it is just a reference to the object itself, del sel will just delete the local reference. If you really want to destroy an object you must destroy it with del on every reference. Python will forget about your object if no living variable is looking at it. You can also overwrite the reference.
>>> node = Node(4)
>>> node = 3# the Node object is erased because you can t have access to it
Overall, I didn't really understand the goal of your Node class except to create linked lists, but as I said, you can do that with list(). In general you don't need to care about what is deleted or not in Python since Python will overwritte the memory if there is no reference left to an object.

Categories

Resources