Comparing values with __eq__ in Python - python

Good day/night, I'm new to Python and I'm making a class called Alpharange that works like the range function but with capital letters only. While I was testing to see if the class worked or not I stumbled upon a strange error. When I set letters1 = Alpharange('D') and letters2 = Alpharange('A', 'D') they printed out the same values like they're supposed to but Python says they aren't equal to each other. To fix this I returned true with the ____eq____ method and while that fixed the error, if I run it with other tests like letters3 = Alpharange('E') and letters4 = Alpharange('B', 'E') it will return true even though they're not equal to each other.
Here is my class as it stands. Is there a better method to use or a way to use the other parameter the same way I can use self?
class AlphabetRange:
def __init__(self, *args):
self.for_loop = tuple(args)
if len(self.for_loop) == 1:
self.start_point = ord('A')
self.end_point = ord(self.for_loop[0])
elif len(self.for_loop) == 2:
self.start_point = ord(self.for_loop[0])
self.end_point = ord(self.for_loop[1])
elif len(self.for_loop) == 3:
self.start_point = ord(self.for_loop[0])
self.end_point = ord(self.for_loop[1])
#Use __iter__ to make obj iterable
def __iter__(self):
if len(self.for_loop) == 1:
self.start_point = ord('A')
return self
elif len(self.for_loop) == 2:
self.start_point = ord(str(self.for_loop[0]))
return self
elif len(self.for_loop) == 3:
self.start_point = ord(self.for_loop[0])
return self
def __next__(self):
#the_end = self.end_point
if self.start_point < self.end_point:
if len(self.for_loop) == 1:
lets = chr(self.start_point)
self.start_point += 1
return lets
elif len(self.for_loop) == 2:
lets = chr(self.start_point)
self.start_point += 1
return lets
elif len(self.for_loop) == 3:
lets = chr(self.start_point)
self.start_point += self.for_loop[2]
return lets
else:
raise StopIteration
def __eq__(self, other):
return True

By implementing __eq__ like this you're basically telling python that whatever other is doesn't matter, you objects equals it. What you instead want to do is implement actual logic in it:
def __eq__(self, other):
return self.start_point == other.start_point and self.end_point == other.end_point
Sidenote: my code is ment to illustrate some type of logic which could be used. Please double check how you could tell similar objects apart and use that to determine how __eq__ should work.

Related

How to use multiple comparison operators at once

class Fraction:
def __init__(self, top, bottom):
self.top = top
self.bottom = bottom
def __repr__(self):
return f"{self.top}/{self.bottom}"
def __ne__(self, other):
ne_first_top = self.top * other.bottom
ne_second_top = self.bottom * other.top
return ne_first_top != ne_second_top
def __eq__(self, other):
first_top = self.top * other.bottom
second_top = other.top * self.bottom
return first_top == second_top
f1 = Fraction(2, 3)
f3 = Fraction(1, 4)
assert f1 != f3 == True
When I run this code, I get the error AttributeError: 'bool' object has no attribute 'bottom'. Can I run this code without changing last line?
The last line is incorrect because f3 == True is being evaluated first. It'd work if you did:
assert (f1 != f3) == True
or more simply (because True == True is always True):
assert f1 != f3
Technically you could make it "work" by forcing f3 == True to return f3, so that f1 != (f3 == True) will actually do what you want it to do:
def __eq__(self, other):
if other == True:
return self
but don't do that. It would be extremely silly.
Note that since you've defined __eq__, you don't need to explicitly define __ne__ as its opposite. If __ne__ isn't explicitly defined it will just automatically be interpreted as "not __eq__"; Python doesn't generally force you to do extra work if it can at all be avoided.
If you want to compare objects of different classes, you could amend your eq() method to handle it gracefully. At its simplest, you can do:
def __eq__(self, other):
if type(self) == type(other):
first_top = self.top * other.bottom
second_top = other.top * self.bottom
return first_top == second_top
else:
return False
Of course, if you want Fraction(1,1) == True to return True, then you'll need to elaborate your __eq__() method a bit further.

I can only push two values onto the stack array?

I can only push two values onto the stack array that I have created when I use the myPush() function. The reason I need to do it this way is because I am not allowed to use the built-in stack functions. Plus I have to use an array not a lit. Thank you.
I can only push two values onto the stack array that I have created when I use the myPush() function. The reason I need to do it this way is because I am not allowed to use the built-in stack functions. Plus I have to use an array not a lit. Thank you.
class Stack:
def __init__(self, data):
if data > 0:
self.stack = [0] * data
self.top = -1
self.stack[self.top] = self.stack[-1]
elif data <= 0:
self.stack = [0] * 10
self.top = -1
def showStack(self):
for i in self.stack:
print(i, end=" ")
return
def isEmpty(self):
return self.stack == []
def myEmpty(self): #complete this function
return # just a placeholder
def push(self, data):
self.stack.append(data)
def myPush(self, data):
if data == 0:
print("You did not enter a value.")
elif self.sizeStack() <= 10:
current = self.stack[stack.top]
self.stack[stack.top] = data
self.stack[stack.top - 1] = current
def pop(self):
data = self.stack[-1]
del self.stack[-1]
return data
def myPop(self): #complete this function
return # just a placeholder
def myPeek(self):
temp = self.top
return temp
def sizeStack(self):
return len(self.stack)
userVal = int(input("Enter the size of the stack: "))
stack = Stack(userVal)
while True:
print('\n1 display the stack')
print('2 add a value to the stack')
print('3 check the value at the top of the stack')
print('4 remove the value at the top of the stack')
print('5 check if the stack is empty')
print('99 quit')
option = int(input("Enter your choice: "))
if option == 1:
stack.showStack()
elif option == 2:
temp = int(input("Enter a number to add to the stack: "))
stack.myPush(temp)
elif option == 3:
print(stack.peek())
elif option == 99:
break
else:
print('Wrong option')
print
try defining a some customized functions that map to your list. Read up on customization here
class stack:
def __init__(self, data):
self.stack = [0] * data
self.top = -1
# whatever you want to do to init the list
def __str__(self): # replaces your showstack function now just use print(stack)
return self.stack
def __iter__(self): # creates an iterator you can use
return iter(self.stack)
def __len__(self): # replaces your sizestack function
return len(self.stack)
basically just adjust the methods you need.

How to define an indexing method within a class (__getitem__ attempted)

I am new to oop in python.
Below is a class for a mathod that is similar to range() except that it is inclusive for the range boundary.
I am trying to create an indexing method inside the class so that when a specific index is called the element with that index is returned. I read that __getitem__ can perform indexing yet I have not been successful in implementing it correctly. If there is a more efficient way not necessarily using __getitem__ please advise.
Please take a look at the code below, this is a simple code aimed at learning how create classes.
the method starting at def __getitem__(self,index) is the one that does not work and this corresponds to calling the index at the end o[4] which is what I would like to achieve.
class inclusive_range:
def __init__(self, *args):
numargs = len(args)
if numargs < 1: raise TypeError('requires at least one argument')
elif numargs == 1:
self.stop = args[0]
self.start = 0
self.step = 1
elif numargs == 2:
(self.start,self.stop) = args
self.step = 1
elif numargs == 3:
(self.start,self.stop,self.step) = args
else:
raise TypeError('three arguments required at most, got {}'.format(numargs))
def __iter__(self): # this makes the function (the method) and iterable function
i = self.start
while i <= self.stop:
yield i
i += self.step
def __getitem__(self,index):
return self[index]
print(self[index])
def main():
o = inclusive_range(5, 10, 1)
for i in o: print(i, end=' ')
o[2]
if __name__ == "__main__": main()
Thank you
You can just calculate the number based on self.start, the index and the step size. For your object to be a proper sequence you also need a length, which comes in handy when testing for the boundaries:
def __len__(self):
start, stop, step = self.start, self.stop, self.step
if step < 0:
lo, hi = stop, start
else:
lo, hi = start, stop
return ((hi - lo) // abs(step)) + 1
def __getitem__(self, i):
length = len(self)
if i < 0:
i += length
if 0 <= i < length:
return self.start + i * self.step
raise IndexError('Index out of range: {}'.format(i))
The above is based on my own translation of the range() source code to Python, with a small adjustment to account for the end being inclusive.
I'd cache the __len__ result in __init__, to avoid having to re-calculate it each time you want to know the length.

Python AttributeError: 'list' object has no attribute after using queue

I am trying to create a BFS solution to the 8 puzzle problem in python. However after I 'get()' a node from the queue and find the puzzle state inside that node it seems the puzzle object has turned into a list object without the method 'getMoves()'. I am wondering why it is not still a Puzzle object?
import collections
import queue
class Node:
def __init__(self, Puzzle, last=None):
self.puzzle = Puzzle
self.last = last
def getPuzzle(self):
return self.puzzle
def getLast(self):
return self.last
class Puzzle:
def __init__(self, startState, goalState):
self.state = startState
self.goal = goalState
def getState():
return self.state
def getMoves(self):
currentState = self.state
possibleNewStates = []
zeroPos = currentState.index(0)
if zeroPos == 0:
possibleNewStates.append(move(0,1))
possibleNewStates.append(move(0,3))
elif zeroPos == 1:
possibleNewStates.append(move(1,0))
possibleNewStates.append(move(1,2))
possibleNewStates.append(move(1,4))
elif zeroPos == 2:
possibleNewStates.append(move(2,1))
possibleNewStates.append(move(2,5))
elif zeroPos == 3:
possibleNewStates.append(move(3,0))
possibleNewStates.append(move(3,4))
possibleNewStates.append(move(3,6))
elif zeroPos == 4:
possibleNewStates.append(self.move(4,1))
possibleNewStates.append(self.move(4,3))
possibleNewStates.append(self.move(4,5))
possibleNewStates.append(self.move(4,7))
elif zeroPos == 5:
possibleNewStates.append(move(5,2))
possibleNewStates.append(move(5,4))
possibleNewStates.append(move(5,8))
elif zeroPos == 6:
possibleNewStates.append(move(6,3))
possibleNewStates.append(move(6,7))
elif zeroPos == 7:
possibleNewStates.append(move(7,4))
possibleNewStates.append(move(7,6))
possibleNewStates.append(move(7,8))
else:
possibleNewStates.append(move(8,5))
possibleNewStates.append(move(8,7))
return possibleNewStates
def move(self, current, to):
changeState = self.state
save = changeState[to]
changeState[to] = changeState[current]
changeState[current] = save
return changeState
def printPuzzle(self):
copyState = self.state
print(copyState)
'''
for i in range(9):
if i == 2 or i == 5:
print((str)(copyState[i]))
else:
print((str)(copyState[i])+" ", end="")
print()
'''
def isSolved(self):
return self.state == self.goal
class Solver:
def __init__(self, Puzzle):
self.puzzle = Puzzle
def solveBFS(self):
puzzle = self.puzzle
startNode = Node(puzzle)
myQueue = queue.Queue(0)
myQueue.put(startNode)
myPuzzle = startNode.getPuzzle()
print(myPuzzle.isSolved())
while myQueue:
currentNode = myQueue.get()
currentPuzzle = currentNode.puzzle
if currentPuzzle == [0,1,2,3,4,5,6,7,8]:
return currentNode
nextMoves = currentPuzzle.getMoves() # << ERROR HERE
for state in nextMoves:
nextNode = Node(state, currentNode)
myQueue.put(nextNode)
startingState = [7,2,4,5,0,6,8,3,1]
goalState = [0,1,2,3,4,5,6,7,8]
myPuzzle = Puzzle(startingState, goalState)
mySolver = Solver(myPuzzle)
goalNode = mySolver.solveBFS()
goalPuzzle = goalNode.getPuzzle()
goalPuzzle.printPuzzle()
That's because in some cases, state must be a list when you create a new node:
nextMoves = currentPuzzle.getMoves()
for state in nextMoves:
nextNode = Node(state, currentNode)
Here state must've been a list returned from the Puzzle.getMoves() list:
def getMoves(self):
# ...
possibleNewStates = []
# append various objects to possibleNewStates.
return possibleNewStates
In getMoves() you use two callables to produces new states:
possibleNewStates.append(move(0,1))
and
possibleNewStates.append(self.move(4,1))
Puzzle.move() produces a list; you do some swapping on self.state which you set to a list:
def move(self, current, to):
changeState = self.state
save = changeState[to]
changeState[to] = changeState[current]
changeState[current] = save
return changeState
Note that this mutates self.state in-place; you did not create a copy of the value here.
self.state was set from the first argument to Puzzle(), which was a list:
startingState = [7,2,4,5,0,6,8,3,1]
goalState = [0,1,2,3,4,5,6,7,8]
myPuzzle = Puzzle(startingState, goalState)
so self.move() returns a list, not a Puzzle() instance.
You didn't share the definition of the move() global function; if this also produces a list then that's another cause of your Node.puzzle values becoming lists.
You probably want to correct all move() calls to use self.move(), and for self.move() to return a new Puzzle() instance. You'll need to create a copy of self.state list here too:
def move(self, current, to):
changeState = self.state[:] # create a copy
changeState[to], changeState[current] = changeState[current], changeState[to]
return Puzzle(changeState, self.goal)
All move() in getMoves() should be changed to self.move(). Otherwise you are working with an unbound Puzzle.

Recursion and return statements in python

I am trying to implement a 2-3 tree but I am having trouble with the find method.
This method given an int as parameter should return the node that contains the int.
The problem is that sometimes it works, sometimes it does't and I don't know why.
I have added a test print. For a particular int that I know for sure that is part of the tree, the code executes the print statement, meaning that it has found the node, but does not return this node. Instead it return False which is at the end of the code.
Can you help me solving this ?
def find(self,data,node=0): #return problem ???
if node==0:
a=self.root
else:
a=node
if a.data2==None:
if data==a.data: ### here is the problem
print("qwertyuiop") ### it does not execute the return statement
return a
elif data < a.data:
if a.left!=None:
return self.find(data,a.left)
elif data > a.data:
if a.right!=None:
return self.find(data,a.right)
else:
if a.data2!=None:
if (data==a.data or data==a.data2):
return a
elif data<a.data:
if a.left!=None:
return self.find(data,a.left)
elif (data>a.data and data<a.data2):
if a.middle!=None:
return self.find(data,a.middle)
elif data>a.data2:
if a.right!=None:
return self.find(data,a.right)
print("Not Found") ### function executes this print
return False
self.root is the root of the tree and is an object of the following class
class Node:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.data2 = None
self.data3 = None
self.left = left
self.right = right
self.middle = None
self.middle2 = None
Binary Search Tree:
class Nodeee:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
class BST:
def __init__(self, root=None):
self.c=[]
self.total=0
self.root = None
def parent(self,data,node=5):
def search(nodee,cc,data):
if data==cc.data:
return nodee
else:
if data<cc.data:
nodee=cc
return search(nodee,cc.left,data)
elif data>cc.data:
nodee=cc
return search(nodee,cc.right,data)
print("Parent Error")
return False
if node==self.root:
print("Root has no parent")
else:
a=self.root
c=self.root
return search(a,c,data)
def lookup(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data < a.data:
if a.left==None:
return a
else:
return self.lookup(data,a.left)
elif data > a.data:
if a.right==None:
return a
else:
return self.lookup(data,a.right)
def find(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data==a.data:
print("WTF")
return a
elif data < a.data:
if a.left!=None:
return self.find(data,a.left)
elif data > a.data:
if a.right!=None:
return self.find(data,a.right)
print("Not Found")
return False
def find2(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data==a.data:
return True
elif data < a.data:
return self.find2(data,a.left)
elif data > a.data:
return self.find2(data,a.right)
return False
def is_empty(self):
if self.root==None:
return True
def is_leaf(self,n):
if (n.left==None and n.right==None):
return True
return False
def delete(self):
self.root=None
def insert(self, data):
if self.root==None:
self.root=Nodeee(data)
self.total+=1
return True
else:
b=self.lookup(data)
if data < b.data:
b.left=Nodeee(data)
self.total+=1
return True
elif data > b.data:
b.right=Nodeee(data)
self.total+=1
return True
print("Insert Error !")
return False
def inorder_swap(self,data):
a=self.find(data)
b=a.right
while self.is_leaf(b)!=True:
if b.left!=None:
b=b.left
elif b.left==None:
b=b.right
temp=a.data
a.data=b.data
b.data=temp
def remove(self,data):
a=self.find(data)
if self.is_leaf(a)==True:
b=self.parent(data)
if b.left==a:
b.left=None
elif b.right==a:
b.right=None
elif self.is_leaf(a)==False:
if a.left==None:
b=self.parent(data)
if b.left==a:
b.left=b.left.right
elif b.right==a:
b.right=b.right.right
elif a.right==None:
b=self.parent(data)
if b.left==a:
b.left=b.left.left
elif b.right==a:
b.right=b.right.left
elif (a.left!=None and a.right!=None):
self.inorder_swap(data)
self.remove(data)
def inorder(self,node):
if node!=None:
self.inorder(node.left)
self.c.append(node.data)
self.inorder(node.right)
def inorder_print(self):
self.c=[]
self.inorder(self.root)
print("\nStart")
for x in range(len(self.c)):
print(self.c[x], end=",")
print("\nFinish\n")
a=BST()
print(a.insert(234)==True)
print(a.insert(13)==True)
print(a.insert(65)==True)
print(a.insert(658)==True)
print(a.insert(324)==True)
print(a.insert(86)==True)
print(a.insert(5)==True)
print(a.insert(76)==True)
print(a.insert(144)==True)
print(a.insert(546)==True)
print(a.insert(2344)==True)
print(a.insert(1213)==True)
print(a.insert(6345)==True)
print(a.insert(653348)==True)
print(a.insert(35324)==True)
print(a.insert(8463)==True)
print(a.insert(5555)==True)
print(a.insert(76539)==True)
print(a.insert(14499)==True)
print(a.insert(59999946)==True)
a.inorder_print()
a.remove(35324)
a.remove(1213)
a.remove(2344)
a.remove(144)
a.remove(5555)
a.remove(6345)
a.remove(59999946)
a.remove(76)
print(a.root.data)
a.inorder_print()
def inorder_swap(self,data):
a=self.find(data)
b=a.right
while self.is_leaf(b)!=True:
if b.left!=None:
b=b.left
elif b.left==None:
b=b.right
temp=a.data
a.data=b.data
b.data=temp
a here is the node containing the passed data. This method does nothing else than swapping a's data with some leaf's data (first one while finds), thereby distorting the tree order. A follow-up find on the same data therefore fails and returns False. Since your code has no error checks, this results in an AttributeError.
You probably want to move nodes around in inorder_swap. However you only assign to the local name b. If you want to change nodes, then you need to use b.left = or b.right =.
It might be that there are more problems, that I don't see right now.
Also your code has several style problems, some of them:
You have four functions doing the same: find, find2, lookup and search in parent.
Most of the naming is not informative or even confusing.
Lines like if a.right==None: should be written as if not a.right: (or maybe if a.right is None:).
Check the return value of functions and don't just assume they return a valid node if they might not (i.e. find might return False instead of a node). Or alternatively use exception handling.
If you have an if ... elif ... elif block you don't have to check the last possibility if it is sure to be true, for example:
if b.left!=None:
# something
elif b.left==None:
# something else
should be
if b.left:
# something
else:
# something else

Categories

Resources