I am getting UnboundLocalError: local variable 'prev' referenced before assignment on the given code below
class Node:
def __init__(self,value,next=None):
self.value=value
self.next=next
class MyList:
def __init__(self,a):
self.head=None
tail=None
for i in a:
n=Node(i,None)
if self.head is None:
self.head=n
tail=n
else:
tail.next=n
tail=n
def showList(self):
n=self.head
if n==None:
print("Empty list")
while n!=None:
print(n.value)
n=n.next
def isEmpty(self):
n=self.head
if n is None:
print("True")
else:
print("False")
def clear(self):
while(self.head!=None):
temp=self.head
self.head=self.head.next
temp=None
def insert(self, newElement):
node = self.head
while node:
if node.value == newElement:
print("Value already exists")
return False
prev = node
node = node.next
if prev:
prev.next = Node(newElement)
else:
self.head = Node(newElement)
return True
list1 = [1, 2, 3, 4, 5]
linklist = MyList(list1)
linklist.showList()
linklist.isEmpty()
linklist.clear()
linklist.showList()
linklist.insert(6)
linklist.showList()
If I remove the clear(self) function the code is working perfectly but when it is not removed an error is showing in the insert(self,newElement) function. It is showing
Traceback (most recent call last):
File "c:\Users\acer\Desktop\Untitled-1.py", line 65, in <module>
linklist.insert(6)
File "c:\Users\acer\Desktop\Untitled-1.py", line 51, in insert
if prev:
UnboundLocalError: local variable 'prev' referenced before assignment
PS C:\Users\acer>
In your insert method, you need to initialize prev to None before entering the loop. Otherwise, if the loop exits before the first iteration (i.e., if self.head is None), then prev will be undefined, which is what Python is telling you. Just add the initialization like so:
def insert(self, newElement):]
prev = None
node = self.head
while node:
...
In your insert() method if you do not enter the while loop then the prev variable will not be initialised.
What you need to do is initialise it to something at the start of your insert() method or ensure that you are always entering the while loop.
Related
I am trying to write function which remove and returns the last item from the linked list.
The code is successfully removing the last time, but I am not sure whether it is returning the correct item.
I have tried printing the current
print(current)
return current
returns the last node
mylist.pop()
print(current)
Throws the error: current is not defined
if I print
print(mylist.pop())
Output:
<__main__.Node object at 0x000001EB42303CD0>
My 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
def __str__(self):
res = ''
temp = self.head
while temp:
res += str(temp.data) + " "
temp = temp.next
return res[:-1]
def pop(self):
print(f'Original Linked List: {self}')
current = self.head
previous = None
while current.getNext() != None:
previous = current
current = current.getNext()
print('Popped item is: %d' % (current.data))
previous.setNext(current.getNext())
print(f'New Linked List after pop: {self}')
return current
print(mylist.pop()) is correct, you just need to better understand what the printed message means. Try implementing __str__ or __repr__ in your Node class, it will be clearer.
This code deals with removal of duplicates from a linked list in Python. The problem seems to be in the remove function.
class Node(object):
def __init__(self, data = None, next_node = None):
self.next_node = next_node
self.data = data
#get data at that location
def get_data(self):
return self.data
#get next element in linked list
def get_next(self):
return self.next_node
#point to node specified by argument
def set_next(self, new_next):
self.next_node = new_next
class LinkedList(object):
def __init__(self, head = None):
self.head = head
#insert element in linked list
def insert(self, data):
new_node = Node(data)
new_node.set_next(self.head)
self.head = new_node
#remove duplicates
def remove(self):
#point to head
current = self.head
previous = None
removed = False
#variable to compare the current data with the rest
new = current
new = new.get_next()
#while current is not None
while current:
if current.get_data() != new.get_data():
previous = new
new = new.get_next()
#if same data, delete extra node from list
else:
removed = True
#if only one element in list
if previous is None:
self.head = new.get_next()
else:
previous.set_next(new.get_next())
new = new.get_next()
#if 'new' reaches end of list, do this
if new is None:
current = current.get_next()
previous = current
new = current
new = new.get_next()
if not removed:
print("No duplicates!")
#print resulting linked list
def print_result(self):
current = self.head
while current:
print(current.get_data(), end = " ")
current = current.get_next()
(I have ignored the 'function calling' part of the code).
I am getting an attribute error at the first if statement after while current: (in the remove function) saying:
Traceback (most recent call last):
File "python", line 64, in <module>
File "python", line 26, in remove
AttributeError: 'NoneType' object has no attribute 'get_data'
I can't understand which is None and why. Any help is greatly appreciated!
Your general approach appears correct, assuming you're OK with an exponential running time, but there are some details that will cause crashes. Here's a couple I spot offhand:
If the list is length 1, if current.get_data() != new.get_data(): will crash because new is None.
These lines:
current = current.get_next()
previous = current
new = current
new = new.get_next() # boom!
will crash when you reach the end of the list. current is the last node and you get the next, which is None, and then attempt to None.get_next().
To fix these, proceed through your list one node at a time and check for None every time you next to avoid crashing. Same goes for unlinking: only unlink one node at a time by keeping prev where it is and setting prev.next_node and curr to curr.next, then test if curr is None before doing anything else.
Here's a simple, working version:
def remove(self):
curr = self.head
while curr:
runner = curr.next_node
prev = curr
while runner:
if runner.data == curr.data:
prev.next_node = runner.next_node
else:
prev = runner
runner = runner.next_node
curr = curr.next_node
The idea is to use curr to step through the list node by node. For every node, create a runner and prev which will iterate through the remainder of the list node by node and unlink any nodes that match curr.
There's also a linear approach using a set (trading space for speed):
def remove_linear(self):
seen = set()
curr = self.head
prev = None
while curr:
if curr.data not in seen:
seen.add(curr.data)
prev = curr
else:
prev.next_node = curr.next_node
curr = curr.next_node
Try it!
A last note: Python generally doesn't use getters and setters; they add verbosity and don't offer any genuine protection, so I omitted them in my code above. Trust your client and use underscore prefixes for "private" variables.
I've hit a snag working with classes on a linked list. My code below:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Item(object):
def __init__(self, data, next_item = None):
self.data = data
self.next_item = next_item
def get_item(self):
return self.data
def set_next(self, setnext):
self.next_item = setnext
def get_next(self):
return self.next_item
class LinkedList(object):
def __init__(self):
self.head = None
def add(self,item):
temp = Item(item)
temp.set_next(self.head)
self.head = temp
def find(self, item):
current = self.head
while current != None:
if current == item:
print "Found It!"
else:
current = current.get_next()
def print_list(self):
node = self.head
while node:
print node.get_item()
node = node.get_next()
def size(self):
counter = 0
current = self.head
while current != None:
counter += 1
current = current.get_next()
print counter
def insert(self,item,lpos):
current = self.head
while current != lpos:
current = current.get_next()
if current == None:
return None
else:
item_insert = Item(item, lpos.next_item())
lpos.set_next(item_insert)
myList = LinkedList()
myList.add(1)
myList.add(2)
myList.add(3)
myList.insert(8,2)
When i run this code the method (insert) fails with the following error:
Traceback (most recent call last):
File "main.py", line 72, in <module>
myList.insert(8,2)
File "main.py", line 56, in insert
item_insert = Item(item, lpos.Item.next_item())
AttributeError: 'int' object has no attribute 'Item'
The insert method will allow you to add a node to your linked list at a specified point, and carry out with rearranging the proper pointers, considering the insert.
please advise!
You haven't pay attention to the difference between 'item' and 'index'. The index is a unsigned digit present for the position of the item in the list, however the item is a node in the list.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Item(object):
def __init__(self, data, next_item = None):
self.data = data
self.next_item = next_item
def get_item(self):
return self.data
def set_next(self, setnext):
self.next_item = setnext
def get_next(self):
return self.next_item
class LinkedList(object):
def __init__(self):
self.head = None
def add(self,item):
temp = Item(item)
temp.set_next(self.head)
self.head = temp
def find(self, item):
current = self.head
while current != None:
if current == item:
print "Found It!"
else:
current = current.get_next()
def print_list(self):
node = self.head
while node:
print node.get_item()
node = node.get_next()
def size(self):
counter = 0
current = self.head
while current != None:
counter += 1
current = current.get_next()
print counter
def insert(self,item,lpos):
if lpos == 0:
item_insert = Item(item, self.head)
self.head = item_insert
return
current = self.head.get_next()
previous = self.head
index = 1
while index != lpos:
index += 1
previous = current
current = current.get_next()
if current == None:
return None
item_insert = Item(item, current)
previous.set_next(item_insert)
myList = LinkedList()
myList.add(1)
myList.add(2)
myList.add(3)
myList.insert(8,0)
myList.print_list()
lpos is an index, which type is int. But what you want to set is Item.next_item(), of course it doesn't work. Change:
# lpos is Int
item_insert = Item(item, lpos.next_item())
to:
# use Item instance to do your stuff
item_insert = Item(item, current.next_item())
Anyway, your implementation of insert should not be correct.
I'm new to python and have been trying to learn simple data structures. I've been able to hack together some functions for a linked list and have been having trouble with my delete function. Heres list with the function in question and the test case:
class Node:
def init(self, initial_data):
self.data = initial_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
class LinkedList:
def init(self):
self.head = None
def __str__(self):
output_string = ''
current = self.head
while current is not None:
output_string += str(current.get_data())
next_node = current.get_next()
#gives the pointer to the next node i.e. the next node is that which is next to the current
if next_node is not None:
output_string += "->"
current = next_node
return output_string
#does not need to be changed for ordered linked list
def is_empty(self):
if self.head is None:
return True
else:
return False
def insert(self, data):
current = self.head
previous = None
stop = False
while current != None and not stop:
if current.get_data() > data:
stop = True
else:
previous = current
current = current.get_next()
temp = Node(data)
if previous == None:
temp.set_next(self.head)
self.head = temp
else:
temp.set_next(current)
previous.set_next(temp)
#does not need to be changed for ordered linked list
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
stop = False
while current is not None and not found and not stop:
if current.get_data() == item:
found = True
else:
current = current.get_next()
return found
def delete(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 is None:
self.head = current.get_next()
else:
previous.set_next(current.get_next())
def test_nonexistent():
my_list = LinkedList()
my_list.insert(31)
my_list.insert(77)
my_list.insert(17)
my_list.insert(93)
my_list.insert(26)
my_list.insert(54)
assert my_list.size() == 6
my_list.delete(77)
my_list.delete(1)
assert my_list.size() == 5
I get the error message
"AttributeError: 'NoneType' object has no attribute 'get_data' with
delete Function"
I believe there is something wrong with the delete function as it can't handle a value thats isn't in the list, but I'm stumped as to how to get it to work at this point. Any help is appreciated!
You are using None to indicate there are no further nodes in the list, i.e. that you're at the end, but you never test for that when you're searching the list, so when you reach the end you try to call None.get_data(). Solution: don't do that.
A good way to rework the loop might be to change the while loop to test current and use break to exit the loop when the item is found.
while current is not None:
if current.get_data() == item:
break
else:
previous = current
current = current.get_next()
You'll need to redo some of the later logic to account for this. You don't need the separate found flag; you can simply check current. If it's None, you reached the end of the list without finding the item you were looking for. Otherwise, you found it, and current is it.
It's a bit hard to figure this out as you haven't posted the actual LinkedList but I assume that you are calling current.get_next() too many times and you probably reach the end of the list. Maybe something like this does the magic for you (UPDATE):
def delete(self, item):
current = self.head # we don't need the previous variable
previous = None
found = False
while current is not None and not found:
if current.get_data() == item:
found = True
else:
previous = current
current = current.get_next()
# at this point we should have either reached to end of the list
# and current is None
# or we have found the node we were looking for
if found and previous is not None:
# delete it
previous.set_next(current.get_next())
elif found and previous is None:
# removing head element
self.head = None
else:
# nothing found
print("Do whatever you want here")
So i solved a couple of problems already, by getting help here, and
from people i know. the root of my problems is that i don't know how to wrap None so
that i don't keep getting these errors of not having the attribute, or not callable.
For this linked list, all i really need is insert and printlist.
I didn't include print list because it is simple, and is not causing problems.
The error is under Linked_List, under insert, under the elif.
It's commented so: #<----ERROR
Here is the code:
class Node:
def __init__(self, word):
self.data = word
self.next = None
def nextNode(self):
if self.next is not None:
return self.next
else:
return None
def getData(self):
return self.data
def setNext(self, node):
self.next = node
def hasNext(self):
if self.next == None:
return False
else:
return True
class Linked_List:
def __init__(self):
self.head = Node(None)
self.isempty = True
def insert(self, word):
newNode = Node(word)
prev = self.head.nextNode()
current = self.head.nextNode()
nextFound = False #the next would be the current when it is less than node
#Look for position to insert:
#When empty
if self.isempty == True:
self.isempty = False
self.head = newNode
#When has more than one
elif self.head.hasNext():
while nextFound == False:
if current.getData() > newNode.getData():
prev = current
current = curent.nextNode()
else:
nextFound = True
#Insert
prev.next().setNext(newNode) # <-------ERROR -----HERE~~
newNode.setNext(current)
else:
#When only has one node not empty
if self.head.getData() > newNode.getData():
self.head.setNext(newNode)
else:
newNode.setNext(self.head)
self.head = newNode
Insertion:
lList.insert(string)
Solved Here:
class Linked_List:
def __init__(self):
self.head = Node(None)
self.isempty = True
def insert(self, word):
newNode = Node(word)
prev = self.head.nextNode()
current = self.head.nextNode()
nextFound = False #the next would be the current when it is less than node
#Look for position to insert:
#When empty
if self.isempty == True:
self.isempty = False
self.head = newNode
#When has more than one
elif self.head.hasNext():
while nextFound == False and current != None:
if current.getData() > newNode.getData():
prev = current
if current.hasNext():
current = current.nextNode()
else:
current = None
else:
nextFound = True
#Insert
prev.setNext(newNode)
newNode.setNext(current)
else:
#When only has one node not empty
if self.head.getData() > newNode.getData():
self.head.setNext(newNode)
else:
newNode.setNext(self.head)
self.head = newNode
How do you use it? I'm guessing that you do something like yourList.insert(1). In your code you do: self.head = node, where node is what user have passed to insert. So, on the next call to insert you end up trying to call an int or anything you've tried to put into the list. You need to wrap any objects given by the user with your Node class:
def insert(self, thing):
node = Node(thing)
//...
However, please remember to post all of the relevant code, so the people trying to help you won't have to guess.
EDIT: still, after the edit the case remains the same. You don't wrap objects passed to your list, so you keep trying to call Node methods on non-Node objects...