How to use multiple comparison operators at once - python

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.

Related

Comparing values with __eq__ in 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.

Combination Lock Program

Me and my partner have been working on this for a few hours and can't figure this out. The directions are vague in some areas and our professor did not do a good job of breaking it down to help us. Here is a link to the directions. I believe they are not very clear but please correct me if I am wrong and just overthinking it https://imgur.com/a/huHnwos
I believe that our biggest problems are the unlock(combination) and set_new_combination(new_combination) methods. I can figure out the str() method as that one isn't very hard to do. We've tried the things our professor has told us to try but they have been unsuccessful.
class Lock:
def __init__(self, combination = 0):
self.combination = combination
self.locked = False
def lock(self):
self.locked = True
def unlock(self, combination):
if combination == True or combination == 0:
self.locked = False
def set_new_combination(self, new_combination):
if self.locked == False:
self.combination = new_combination
def is_locked(self):
if self.locked == True or self.combination == True:
return True
else:
return False
def __eq__(self, other):
if other is not None and type(other) == type(self):
if self.combination == other.new_combination:
return False
def __str__(self):
return self.combination, ',', self.locked
The expected result should be a working basic combination lock.
Here's my implementation based on the inctructions provided, with comments where it deviates from your code.
class Lock:
def __init__(self, combination = 0): # No change here
self.combination = combination
self.locked = False
def lock(self):
# Although a test of self.locked is redundant, the instructions state
# "...if invoked a second time this, method should do nothing."
if not self.locked:
self.locked = True
def unlock(self, combination):
# You were not testing the stored combination against the one passed to the method.
# it does not matter if the stored combination is zero or a different number,
# you still need to check for equality.
# You also need a test as with lock() to satisfy the "if invoked a second time this,
# method should do nothing" requirement.
if self.locked and self.combination == combination:
self.locked = False
def set_new_combination(self, new_combination):
# You can simply the `if` condition, there's no need to test against False
if not self.locked:
self.combination = new_combination
def is_locked(self):
# I don't know why you are testing the self.combination value, you
# only need to return the state of the lock
return self.locked
def __eq__(self, other):
# You have the correct guard conditions but were returning False when
# the combinations matched. You can simply return the comparison result.
if other is not None and type(other) == type(self):
return self.combination == other.new_combination
def __str__(self):
# For some reason the output format specified for this appears to put it in a list
# (the square brackets) but as it's only for display we'll "fake" the list.
# The `if` statement prints the word 'locked' or 'unlocked' depending on the
# `self.locked` state.
return '[{}, {}]'.format(self.combination, 'locked' if self.locked else 'unlocked')
There are couple of problems with your code. First, if statement in your unlock method will be executed only if combination == 0 or combination == 1, which has nothing to do with lock's combination (self.combination). In your is_locked method you should only return self.locked, no need for if. __eq__ method can also be simplified. And __str__ method should actually return String.
class Lock:
def __init__(self, combination = 0):
self.combination = combination
self.locked = False
def lock(self):
self.locked = True
def unlock(self, combination):
if self.combination == combination:
self.locked = False
def set_new_combination(self, new_combination):
if not self.locked:
self.combination = new_combination
def is_locked(self):
return self.locked
def __eq__(self, other):
return isinstance(other, Lock) and self.combination == other.combination
def __str__(self):
return f'{self.combination}, { "locked" if self.locked else "unlocked"}'
Your unlockmethod is trying to compare a boolean to a number (the combination). Change it to look like this:
def unlock(self, combination):
if combination == self.combination:
self.locked = False
You also did this in your is_locked method, so that should be changed too:
def is_locked(self):
return self.locked
(Any time you find yourself writing something along the lines of if x return True else return False you can almost always replace this with return x if the conditional is simple).
set_new_combination works fine; I don't know what issue you saw with it.
Finally, your __str__ method should actually return a string:
def __str__(self):
return '[' + str(self.combination) + ', ' + 'locked' if self.locked else 'unlocked' + ']'

Concise way for wildcard match among objects?

I have a Match class which needs to implement a wildcard match method. This method will check all the attributes of the class with another object of the same class. If both are same or either is a * then it is a match. This logic is true for all the attributes in a class.
Please refer to the implementation of the wildcard_match method below.
The problem is if I add more attributes to the class, or if the number of attributes is large, I need to manually keep on adding to the method. So I need a concise, DRY way of implementing the method.
Any help is appreciated.
class Match:
def __init__(self):
self.src = "h%s" % random.randint(1, SRC)
self.dst = "h%s" % random.randint(1, DST)
self.proto = random.choice(L4_PROTO)
self.src_port = str(random.randint(2000, 5000))
self.dst_port = random.choice(L4_PORTS)
def __members(self):
return (self.src, self.dst, self.proto, self.src_port, self.dst_port)
def __hash__(self):
return hash(self.__members())
def __eq__(self, other):
""" Exact match check """
if isinstance(other, self.__class__):
return self.__members() == other.__members()
else:
return False
def wildcard_match(self, other):
""" Check whether the two matches are a wildcard match """
if isinstance(other, self.__class__):
if self.src != "*" and other.src != "*" and self.src != other.src:
return False
if self.dst != "*" and other.dst != "*" and self.dst != other.dst:
return False
if self.proto != "*" and other.proto != "*" and self.proto != other.proto:
return False
if self.src_port != "*" and other.src_port != "*" and self.src_port != other.src_port:
return False
if self.dst_port != "*" and other.dst_port != "*" and self.dst_port != other.dst_port:
return False
return True
else:
return False
You can use your classes __dict__ which contains all attributes you defined:
def wildcard_match(self, other):
""" Check whether the two matches are a wildcard match """
if isinstance(other, self.__class__):
for attr_name in self.__dict__:
self_attr = self.__getattr__(attr_name)
other_attr = other.__getattr__(attr_name)
if self_attr != "*" and other_attr != "*" and self_attr != other_attr:
return False
return True
else:
return False

multistep comparison test python

I wanna implement a class overloading and conclude if an event with a given point of time for example 12:59:50 happens before another event so the output is true or false, just a simple comparison test. I implemented it, as you can see, but, i'm pretty much sure this is not the most pythonic or better to say, objected oriented approach to carry out the tasks. I'm new to python so is there any improvement out there ?
Thanks
def __lt__(self, other):
if self.hour < other.hour:
return True
elif (self.hour == other.hour) and (self.minute < other.minute):
return True
elif (self.hour == other.hour) and (self.minute == other.minute) and (self.second < other.second):
return True
else:
return False
Tuples (and other sequences) already perform the type of lexicographic comparison you are implementing:
def __lt__(self, other):
return (self.hour, self.minute, self.second) < (other.hour, other.minute, other.second)
The operator module can clean that up a little:
from operator import attrgetter
def __lt__(self, other):
hms = attrgetter("hour", "minute", "second")
return hms(self) < hms(other)

Method to compare Python dictionaries fails with certain value types?

I can't figure this out. I have two dictionaries which are identical. I use a standard method to determine the differences, of which there should be none. But certain value types are always returned as differences, even when they are not. For example, if a value is a pymongo.bson.ObjectId, the method fails to evaluate it as the same.
d1 = {'Name':'foo','ref1':ObjectId('502e232ca7919d27990001e4')}
d2 = {'Name':'foo','ref1':ObjectId('502e232ca7919d27990001e4')}
d1 == d2
returns:
True
But:
set((k,d1[k]) for k in set(d1) & set(d2) if d1[k] != d2[k])
returns:
set([('ref1',Objectid('502e232ca7919d27990001e4'))])
So I've figured out that this is weird, no?
d1['ref1'] == d2['ref1'] # True
d1['ref1'] != d2['ref1'] # False
What the?????!?!??!!?
ObjectId('502e232ca7919d27990001e4') creates a new object and by default != compares references. Try for example:
class Obj:
def __init__(self, value):
self.value = value
print Obj(1234) == Obj(1234) # False
This will evaluate to false, because they are difference instances, even if they hold the same value. To make this work, the class must implement the eq method:
class Obj:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return self.value == other.value
print Obj(1234) == Obj(1234) # True
To fix this, you can "monkey-patch" the class:
class Obj:
def __init__(self, value):
self.value = value
print Obj(1234) == Obj(1234) # False
Obj.__eq__ = lambda a, b: a.value == b.value
print Obj(1234) == Obj(1234) # True
Or compare them by their values directly.
print Obj(1234).value == Obj(1234).value
Compare the values when possible because monkey-patching may break seemingly unrelated code.

Categories

Resources