How to write equals method - python

Situation: I'm trying to get a good handle on the doubly-linked structure. I've got a decent grip on the methods so far. I want to be able to create two objects for this class and check if every item in it is equal. I don't have any syntax errors, and the error I'm getting is kind of confusing. So here's what I have so far.
class LinkedList:
class Node:
def __init__(self, val, prior=None, next=None):
self.val = val
self.prior = prior
self.next = next
def __init__(self):
self.head = LinkedList.Node(None) # sentinel node (never to be removed)
self.head.prior = self.head.next = self.head # set up "circular" topology
self.length = 0
def append(self, value):
n = LinkedList.Node(value, prior=self.head.prior, next=self.head)
n.prior.next = n.next.prior = n
self.length += 1
def _normalize_idx(self, idx):
nidx = idx
if nidx < 0:
nidx += len(self)
if nidx < -1:
raise IndexError
return nidx
def __getitem__(self, idx):
"""Implements `x = self[idx]`"""
nidx = self._normalize_idx(idx)
currNode = self.head.next
for i in range(nidx):
currNode = currNode.next
if nidx >= len(self):
raise IndexError
return currNode.val
def __setitem__(self, idx, value):
"""Implements `self[idx] = x`"""
nidx = self._normalize_idx(idx)
currNode = self.head.next
if nidx >= len(self):
raise IndexError
for i in range(nidx):
currNode = currNode.next
currNode.val = value
def __iter__(self):
"""Supports iteration (via `iter(self)`)"""
cursor = self.head.next
while cursor is not self.head:
yield cursor.val
cursor = cursor.next
def __len__(self):
"""Implements `len(self)`"""
return self.length
def __eq__(self, other):
currNode = self.head.next
currNode2 = other.head.next
for currNode, currNode2 in zip(self, other):
if currNode.val != currNode2.val:
return False
return True
Test:
from unittest import TestCase
tc = TestCase()
lst = LinkedList()
lst2 = LinkedList()
tc.assertEqual(lst, lst2)
lst2.append(100)
tc.assertNotEqual(lst, lst2)
When I test this code I get I get an Assertion error saying " [] == [100] " I'm unsure why my code recognizes this as equal, when I want it to actually check specific values in the node.

zip only goes as far as the shortest list. You want itertools.zip_longest, and you don't want .val (your iterator returns the actual values already). Try this:
def __eq__(self, other):
for val1, val2 in zip_longest(self, other):
if val1 != val2:
return False
return True
or perhaps better?
def __eq__(self, other):
return all(val1 == val2 for val1, val2 in zip_longest(self, other))
EDIT
I like #BrenBarn's suggestion of checking length first. Here's a more efficient answer:
def __eq__(self, other):
return len(self) == len(other) and all(
val1 == val2 for val1, val2 in zip(self, other))

zip(self.other) only gives you as many elements as the shorter of the two lists. It discards the extra part of the longer list. So for [] == [100], zip doesn't give any elements, and your code returns True without checking anything.
You could just do a check at the beginning to see if the lists have different length. If they do, they can't be equal.

Related

Reshaping a Linked List

Hello I am working on an assignment and stuck with some implementations, the purpose of this is reshaping or modifying a linked list. As you can see from my drawings this will not work as a classical linked list, the drawing 1 is for the classical list appending and I want to do it like the second drawing. There is not a lot of information, similar code to the drawing 2 will help a lot.
class Node:
"""Node class for the linked list"""
def __init__(self, val: Any, next_node: "Node"):
self.val = val
self.next_node = next_node
def __repr__(self):
return f"({self.val})->{self.next_node}"
class LinkedList:
"""Linked list class"""
def __init__(self, iterable: Optional[Iterable] = None):
self.head = None
self._length = 0
if isinstance(iterable, (list, tuple, str)):
for item in iterable:
self.push(item)
elif iterable is not None:
raise TypeError("If providing an input, it must be an iterable.")
def __len__(self):
return self._length
def __repr__(self):
return self.display()
def push(self, val):
self.head = Node(val, self.head)
self._length += 1
def pop(self):
if not self.head:
raise IndexError("Cannot pop from an empty list.")
popped_value = self.head.val
self.head = self.head.next_node
self._length -= 1
return popped_value
def size(self):
return self._length
def search(self, val):
current_node = self.head
while current_node.val != val:
current_node = current_node.next_node
if current_node is None:
break
return current_node
def remove(self, val):
node = self.search(val)
if node is None:
raise ValueError("Value does not exist in list.")
node.val = node.next_node.val
node.next_node = node.next_node.next_node
self._length -= 1
return self
def display(self):
return f"({self.head.val})->{self.head.next_node}"
As can see, the node A is linked to B, node B linked to C, node C is linked toA. Okay, this segment is a circular list.
But, you add two node: D and E.
D is linked to E and linked to A. A is linked to E.
Here I highlight 2 points:
D have two links: E and A.
A node can be accessed by C and D.
This break the definition of linear structure
has a unique “predecessor” and a unique “successor”
So, you don't have a list. You have a Graph!

Issues inserting into arbitrary positions (not beginning or end) of doubly linked list

Ok to preface, this is to help with a school assignment. I understand there are unpythonic methods in the following code but this is the format they insist upon. In my insert method I have 4 cases to account for. All of them are dealt with appropriately except for else and im unsure why. For some reason the method insert isn't updating the linked list to include the new_node when this new_node is not placed at the end or start of the list. I'm unsure why this is as at the appropriate position we store the old value of current, we then store its previous and we set current = new_node, we then set new_node's next to be the old value of current and new_node's previous to be the old currents previous. I'm confused as to why this won't work.
class DLinkedListNode:
# An instance of this class represents a node in Doubly-Linked List
def __init__(self,initData,initNext,initPrevious):
self.data = initData
self.next = initNext
self.previous = initPrevious
if initNext != None:
self.next.previous = self
if initPrevious != None:
self.previous.next = self
def getData(self):
return self.data
def setData(self,newData):
self.data = newData
def getNext(self):
return self.next
def getPrevious(self):
return self.previous
def setNext(self,newNext):
self.next = newNext
def setPrevious(self,newPrevious):
self.previous = newPrevious
class DLinkedList:
# An instance of this class represents the Doubly-Linked List
def __init__(self):
self.__head=None
self.__tail=None
self.__size=0
def search(self, item):
current = self.__head
found = False
while current != None and not found:
if current.getData() == item:
found= True
else:
current = current.getNext()
return found
def index(self, item):
current = self.__head
found = False
index = 0
while current != None and not found:
if current.getData() == item:
found= True
else:
current = current.getNext()
index = index + 1
if not found:
index = -1
return index
def add(self, item):
new_node=DLinkedListNode(item,None,None)
new_node.setNext(self.__head)
self.__head=new_node
current=self.__head
new_node.setPrevious(None)
current=current.getNext()
self.__size+=1
def remove(self, item):
# remove the node containing the item from the list
if self.__size == 0:
raise Exception('List is Empty')
current = self.__head
previous = None
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if not found:
raise Exception('Item not in list')
else:
if previous == None: # the item is in the first node of the list
self.__head = current.getNext()
else: # item is not in the first node
previous.setNext(current.getNext())
self.__size = self.__size -1
def append(self, item):
# adds an item at the end of the list
new_node = DLinkedListNode(item,None,None)
current = self.__head # Start the traversal
if self.__size == 0: # check if list is empty
self.add(item)
else:
while (current.getNext()!=None):
current= current.getNext() # traversing the list
new_node.setNext(None)
new_node.setPrevious(current)
current.setNext(new_node)
self.__size = self.__size +1
def insert(self, pos, item):
# inserts the item at pos
# pos should be a positive number (or zero) of type int
assert type(pos)==int,'Error:pos is not an integer'
assert pos>=0,'Error:pos must be positive'
current=self.__head
new_node= DLinkedListNode(item,None,None)
if pos==0:
self.add(item)
elif pos==self.__size:
self.append(item)
elif pos>self.__size:
raise Exception('Position attempted to enter is larger than the size of linked list.')
else:
current_pos=0
while(current.getNext()!=None):
if (pos)==current_pos:
# storage is a holder variable
storage=current
right=current.getNext()
left=current.getPrevious()
current=new_node
new_node.setPrevious(left)
new_node.setNext(storage)
return True
current=current.getNext()
current_pos+=1
self.__size+=1
# doubly linked list
#Hello(prev)<-->World(store data)-->None
def pop1(self):
current = self.__head
previous = None
while (current.getNext() != None):
previous = current
current = current.getNext()
if (previous == None):
self.__head = None
else:
previous.setNext(None)
self.__size -= 1
return current.getData()
def pop(self, pos=None):
if pos!=None:
assert pos<=self.__size,'Pos must be within list'
assert type(pos)==int,'Pos must be an int'
assert pos>=0,'Pos must not be negative'
current=self.__head
current_pos=0
if pos==(self.getSize()-1) or pos==None:
data_from_method=self.pop1()
return data_from_method
else:
while current.getNext()!=None:
if pos==current_pos:
data=current.getData()
left=current.getPrevious()
right=current.getNext()
left.setNext(right)
right.setPrevious(left)
return data
current_pos+=1
current=current.getNext()
# doubly linked list
#Hello(prev)<-->World(store data)-->None
def searchLarger(self, item):
current=self.__head
current_pos=0
while current.getNext()!=None:
if item<current.getData():
return current_pos
current=current.getNext()
current_pos+=1
return -1
def getSize(self):
return self.__size
def getItem(self, pos):
assert type(pos)==int,'position must be type int'
assert pos<=self.__size,'Position is outside of the list'
current=self.__head
current_pos=0
if pos>=0:
while current!=None:
if current_pos==pos:
return current.getData()
current_pos+=1
current=current.getNext()
else:
current=self.__tail
while current!=None:
if current_pos==pos:
return current.getData()
current_pos-=1
current=current.getPrevious()
def __str__(self):
# returns a string representation of the list
current = self.__head
string = ''
while current != None:
if current.getNext()==None:
string = string + str(current.getData())+''
else:
string=string+str(current.getData())+' '
current = current.getNext()
return string
def test():
new_list=DLinkedList()
for i in range(20):
new_list.insert(i,i)
new_list.insert(1,90)
print(new_list)
test()
You currently have code that links the new node to nodes in the list, but no code that links the existing nodes to the new node. The only gesture you have in that direction is current=new_node, which doesn't achieve anything useful (since current is a local variable and you're about to return from the function).
You probably want something like left.setNext(newnode) and right.setPrevious(newnode), though I'm not sure you've set up both of those variables correctly (one of them should probably be current, unless you're intending to replace an existing node).

Binary Search Tree (Python) not showing result

I'm somewhat new to Python and need help with an issue I'm facing. I'm trying to make a binary search tree. I've written a code and it works, but won't show any result (printed values). I can't figure out what the issue might be. Here is the entire code:
class Node:
def __init__(self, value):
self.value = value
self.left_child = None
self.right_child = None
class binary_search_tree:
def __init__(self):
self.root_node = None
def insert(self, value, curr_node):
if self.root_node == None:
self.root_node == node(value)
elif self.root_node < value:
if self.right_child == None:
self.right.child = value
else:
curr_node == self.right_child
if curr_node < value:
curr_node.right_child = node(value)
elif curr_node > value:
curr_node.left_child = node(value)
else:
print("Error! Value Already Exists!")
elif self.root_node > value:
if self.left_child == None:
self.left.child = value
else:
curr_node == self.left_child
if curr_node < value:
curr_node.right_child = node(value)
elif curr_node > value:
curr_node.left_child = node(value)
else:
print("Error! Value Already Exists!")
else:
print("Error! Value Already Exists!")
def fill_Tree(tree, num_elems = 100, max_int = 1000):
from random import randint
for x in range (num, elems):
curr_elem = randint(0, max_int)
tree.insert(curr_elem)
return tree
I have made a class Node to handle the nodes and a function insert that helps to insert the values. It check for the root node. If its there, it moves onto the leaf based on the values. If not, it adds the value as the root. The program keeps on checking for values and nodes and their differences (less than, greater than etc), just the ways a tree is supposed to function. The program executes, but nothing shows up. Not sure what I'm doing wrong though.
Any sort of help would be appreciated!
Thanks.
If that is your entire code, and if the input and execution is perfect, it would not show any result, because you are not printing any result.
You don't have an apparent main function to create an object of the class binar_search_tree.
The print statements that you have are only when there is an error. If everything works perfectly, your code doesn't print anything
You would need a method that can display the the tree
Currently, your insertion method is assigning values to the left or right children of the root, not traveling to a depth beyond those two nodes, should a value smaller than the left child or greater than the right child be found. Instead, create one class to store the value, left, and right child, along with the necessary insertion method. To determine if a value exists in the tree, it is cleaner to utilize __getitem__ with recursion:
def check_val(f):
def wrapper(cls, _val):
if _val in cls.__class__.seen:
raise ValueError(f"'{_val}' already in '{cls.__class__.__name__}'")
return f(cls, _val)
return wrapper
class Tree:
seen = []
def __init__(self, value=None):
self.left = None
self.value = value
self.right = None
def __lt__(self, _node):
return self.value < getattr(_node, 'value', _node)
#check_val
def insert_val(self, _val):
if self.value is None:
self.value = _val
self.__class__.seen.append(_val)
else:
if _val < self.value:
if self.left is None:
self.left = Tree(_val)
Tree.seen.append(_val)
else:
self.left.insert_val(_val)
else:
if self.right is None:
self.right = Tree(_val)
Tree.seen.append(_val)
else:
self.right.insert_val(_val)
def __getitem__(self, val):
if self.value == val:
return True
if val < self.value:
return getattr(self.left, '__getitem__', lambda _:False)(val)
return getattr(self.right, '__getitem__', lambda _:False)(val)
#classmethod
def load_tree(cls, size = 10):
_t = cls()
import random
for _ in range(size):
_t.insert_val(random.randint(1, 100))
return _t
To run:
t = Tree.load_tree()
print(t.__class__.seen)
#[82, 94, 33, 59, 73, 72, 96, 14, 58, 67]
for i in t.__class__.seen:
assert t[i]
print('all cases passed')
Output:
'all cases passed'

Remove Method Linked List

I am working on a doubly linked list and I can't get my remove function to work correctly. I want to return the value that it is being removed. I have tried various times, with no success. An extra pair of eye would be appreciated.
class DoublyLinked_List:
class __Node:
def __init__(self, val):
self.val = val
self.next = None
self.prev = None
def __init__(self):
self.__header = self.__Node(None)
self.__trailer = self.__Node(None)
self.__header.next = self.__trailer
self.__trailer.prev = self.__header
self.__size = 0
def __len__(self):
return self.__size
def remove_element_at(self, index):
if index > self.__size or index < 0 or index == self.__size:
raise IndexError
current = self.__header.next
if index == 0:
self.__header.next = self.__header.next.next
else:
for i in range(0, index-1):
current = current.next
current.next = current.next.next
self.__size -= 1
return current.val

How do I remove a node from a linked list?

As a first project on OOP I'm working on a linked list oop class, got most of the methods done but the remove node method is not working.
When I run the code I get this error:
AttributeError: 'Node' object has no attribute 'val'
I can't figure out what I'm doing wrong in it!
class Node():
def __init__(self, val):
self.value = val
self.next = None
class Linked_list():
def __init__(self):
self.next = None
nextnode=self.next
def insert(self, val, loc):
p = self
for i in range(0, loc):
p = p.next
tmp = p.next
newNode = Node(val)
p.next = newNode
newNode.next = tmp
def find(self, val):
p = self.next
# loc = 0 # in case we want to return the location
while p != None:
if p.value == val:
return p
else:
p = p.next
#loc=loc+1 # in case we want to return the location
return None
def remove_node(self, node):
current = self.next
previous = None
found = False
while not found:
if current.val == node:
found = True
else:
previous = current
current = current.next
if previous == None:
self.next = current.next
else:
previous.current.next
def __eq__(self, other):
cnt=0
s=self.next
p=other.next
if Linked_list.length(self)!=Linked_list.length(other):
return False
if s.value==p.value:
for i in range(Linked_list.length(self)-1):
p=p.next
s=s.next
if s.value==p.value:
cnt+=1
if cnt==Linked_list.length(self)-1:
return True
else:
return False
Your Node class has an attribute value assigned in the __init__ method, but not an attribute val, hence the error. The confusion probably comes from the fact that the variable you pass to __init__ is called val.

Categories

Resources