Overloading __eq__ in a class - python

I'm trying to overload the == operator in a class, and this is the init method:
class Point:
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
self._fields = ['a','b','c','d']
And I'm trying to overload the == operator, and here is my code for that:
def __eq__(self,right):
if type(right) == type(self):
for i in self._fields:
print(self._fields.index(i))
else:
return False
return True
For == to be true, all the values in init should be the same. So if I have test=Point(1,2,3), and then I have test2 = Point(1,2,3), then test==test2 should return True. However, I have test=Point(1,2,3), and test2=Point(1,1,3) and this is returning True. Can anybody figure out why this is?

You're testing whether self['a'] == right['a'] when what you want is self.a == right.a. You should use the getattr function to do what you want to do.

Currently, all your code does while iterating over the fields is print out their indexes. It only will ever return False for objects of different types. Instead, you should use getattr to get the actual attribute values that correspond to the names in _fields:
def __eq__(self, other):
return (self._fields == other._fields and
all(getattr(self, attr) == getattr(other, attr) for attr in self._fields)
Note that I've changed the test for the two objects having the same type for one that checks that they have the same field (this is a kind of duck-typing). If you want to stick with a type check, I'd make _fields a class attribute, so you will know that every instance has the same value for it.
Alternately, you could do away with the _fields attribute completely and just hard code the attribute names:
def __eq__(self, other):
return (type(self) == type(other) and
self.a == other.a and self.b == other.b and
self.c == other.c and self.d == other.d)

Related

re-use pytest assertion logic for complex objects

How can I reuse the python "smart" reporting for objects?
E.g.:
class Foo(object):
def __init__(self, text, sequence):
self.text = text
self.sequence = sequence
def __eq__(self, other):
return type(self) == type(other) and self.text == other.text and self.sequence == other.sequence
def __ne__(self, other):
return not self.__eq__(other)
What I want is to get the assertion report, just as I would assert the primitive fields, e.g. (of course makes sense to not print attributes that satisfy the comparison operator - and concatenate together the failing attribute reports). Something like:
def test_magic():
> assert Foo(text='a', sequence=[1]) == Foo(text='a', sequence=[1, 2])
E assert Foo(text='a', sequence=[1]) == Foo(text='a', sequence=[1, 2])
E attribute `sequence`:
E [1] == [1, 2]
E Right contains more items, first extra item: 2
E Use -v to get the full diff
Can I do this by writing some plugin, etc and calling in that the internal comparator? Or what is the recommended solution?

How to check if an object exists inside a list of objects?

I am using Python to implement an Earley Parser that has Context Free rules defined as follows:
class Rule:
def __init__(self,string,i,j,dot):
self.i = 0
self.j = 0
self.dot = 0
string = string.split('->')
self.lhs = string[0].strip()
self.rhs1 = string[1].strip()
self.rhs = []
self.rhs1 = self.rhs1.split(' ')
for word in self.rhs1:
if word.strip()!= '':
self.rhs.append(word)
def __eq__(self, other):
if self.i == other.i:
if self.j == other.j:
if self.dot == other.dot:
if self.lhs == other.lhs:
if self.rhs == other.rhs:
return True
return False
To check whether an object of class Rule exists within a chart array or not, I have used the following:
def enqueue(self, entry, state):
if state in self.chart[entry]:
return None
else:
self.chart[entry].append(state)
where chart is an array that is supposed to contain lists of objects of class Rule:
def __init__(self, words):
self.chart = [[] for i in range(len(words))]
Further I check whether a rule exists as that in the chart[entry] as follows (and if it does not exist, then simply append):
def enqueue(self, entry, state):
if state in self.chart[entry]:
return None
else:
self.chart[entry].append(state)
However this gives me an error as
TypeError: 'in <string>' requires string as left operand, not classobj
To circumvent this, I even declared an __eq__ function in the class itself but it doesn't seem to work. Can anyone help me with the same?
Assuming that your object has only a title attribute which is relevant for equality, you have to implement the __eq__ method as follows:
class YourObject:
[...]
def __eq__(self, other):
return self.title == other.title
Of course if you have more attributes that are relevant for equality, you must include those as well. You might also consider implementing __ne__ and __cmp__ for consistent behaviour.

How to treat an object as a numeric type in Python

I sometimes want an object to be treated as a number. This is what I do:
class C(object):
def __init__(self):
self.a = 1.23
self.b = 'b'
def __float__(self):
return float(self.a)
c = C()
Explicit casting works:
works = float(c) + 3.141
correct_comparison = (float(c) < 1)
>>> correct_comparison
False
However, automatic (implicit) casting doesn't work
wrong_comparison = (c < 1)
>>> wrong_comparison
True
doesnt_work = c + 2.718 #TypeError exception is thrown
Is there a way to perform automatic casting in Python. How bad is this idea?
UPDATE #BrenBarn has pointed me to the "emulating numeric types" section in Python documentation. Indeed, it is possible to define every possible operator, which gives a lot of flexibility, but is also very verbose. It seems that automatic implicit casting is possible only if one defines all the relevant operators. Is there a way to make this less verbose?
As #BrenBarn said, you can use inheritance:
class SomeFloat(float):
def __init__(self, *args):
super(float, self).__init__(*args)
self.b = 'b'
It will be not so verbose.
This is not the way Python thinks about objects. There is little value coming casting C to a float because Python usually doesn't care what an object is but how an object behaves. You should implement the custom comparison functions like __lt__ and __eq__. See here. This will also be handy.
The solution may look something like
import functools
#functools.total_ordering
class C(object):
def __init__(self):
self.a = 1.2345
def __float__(self):
return float(self.a)
def __eq__(self, other):
return self.a == float(other)
def __lt__(self, other):
return self.a < float(other)
c = C()
assert c < 3.14
assert 3.14 > c
assert c == 1.23456
assert 1.23456 == c
assert c != 1

overidding Pythons __eq__ method , isistance & eq mothods return false

I'm new to Python from the Java world.
I have written a Python class called "Instance" with 3 properties(attribute, value, and class). I want to override the "eq" method & also the "hash" method, I'm using the "attribute" & "value" properties used for object comparison. I instantiated two objects with the same values, however they return as not equal.
Code is below , Class Instance:
'''Class of type Instance'''
class Instance(object):
__attribute = None;
__value = None;
__classification = None;
#constructor
def __init__(self,attribute,value,classification):
self.attribute = attribute;
self.value = value;
self.classification = classification;
#setters & getters
def setAttribute(self,attribute):
self.attribute = attribute
def setValue(self,value):
self.value = value
def setClassification(self,classification):
self.classification = classification
def getAttribute(self):
return self.Attribute;
def getValue(self):
return self.Value
def getClassification(self):
return self.Classification
def __eq__(self, other):
#if self & other are the same instance & attribute & value equal
return isinstance(self,other) and (self.attribute == other.attribute) and (self.value == other.value)
def __hash__(self):
return hash(self.attribute, self.value)
I'm instantiating in , another Python module called Testing:
if __name__ == '__main__':
pass
from Instance import *
instance1 = Instance('sameValue', 1,'Iris-setosa')
instance2 = Instance('sameValue', 1,'Iris-setosa')
if (instance1 is instance2):
print "equals"
else:
print "not equals"
The program returns: not equals.
Your first problem is isinstance(self, other) isn't asking whether self and other are both instances of compatible types, or whether they're the same instance (as your comment says), it's asking whether self is an instance of the type other. Since other isn't even a type, the answer is always false.
You probably wanted isinstance(self, type(other)). Or maybe something more complicated, like isinstance(self, type(other)) or isinstance(other, type(self)).
Or maybe you don't really want this at all; even for equality testing, duck typing is often a good idea. If other has the same attributes as self, and also hashes to the same value, is that good enough? The answer may be no… but you definitely should ask the question.
Your second problem is a misunderstanding of is:
if (instance1 is instance2):
print "equals"
else:
print "not equals"
The whole point of is is that it's asking whether these are the same object, not whether these two (possibly distinct) objects are equal to each other. For example:
>>> a = []
>>> b = []
>>> a == b
True
>>> a is b
False
They're both empty lists, so they're equal to each other, but they're two different empty lists, which is why you can do this:
>>> a.append(0)
>>> b
[]
And the same is true with your class. Each Instance that you create is going to be a different, separate instance—even if they're all equal.
The __eq__ method that you define customized the == operator. There is no way to customize the is operator.

Unexpected behavior for python set.__contains__

Borrowing the documentation from the __contains__ documentation
print set.__contains__.__doc__
x.__contains__(y) <==> y in x.
This seems to work fine for primitive objects such as int, basestring, etc. But for user-defined objects that define the __ne__ and __eq__ methods, I get unexpected behavior. Here is a sample code:
class CA(object):
def __init__(self,name):
self.name = name
def __eq__(self,other):
if self.name == other.name:
return True
return False
def __ne__(self,other):
return not self.__eq__(other)
obj1 = CA('hello')
obj2 = CA('hello')
theList = [obj1,]
theSet = set(theList)
# Test 1: list
print (obj2 in theList) # return True
# Test 2: set weird
print (obj2 in theSet) # return False unexpected
# Test 3: iterating over the set
found = False
for x in theSet:
if x == obj2:
found = True
print found # return True
# Test 4: Typcasting the set to a list
print (obj2 in list(theSet)) # return True
So is this a bug or a feature?
For sets and dicts, you need to define __hash__. Any two objects that are equal should hash the same in order to get consistent / expected behavior in sets and dicts.
I would reccomend using a _key method, and then just referencing that anywhere you need the part of the item to compare, just as you call __eq__ from __ne__ instead of reimplementing it:
class CA(object):
def __init__(self,name):
self.name = name
def _key(self):
return type(self), self.name
def __hash__(self):
return hash(self._key())
def __eq__(self,other):
if self._key() == other._key():
return True
return False
def __ne__(self,other):
return not self.__eq__(other)
This is because CA doesn't implement __hash__
A sensible implementation would be:
def __hash__(self):
return hash(self.name)
A set hashes it's elements to allow a fast lookup. You have to overwrite the __hash__ method so that a element can be found:
class CA(object):
def __hash__(self):
return hash(self.name)
Lists don't use hashing, but compare each element like your for loop does.

Categories

Resources