Unexpected behavior for python set.__contains__ - python

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.

Related

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.

Overloading __eq__ in a class

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)

how to compare for equality for None objects in custom class in python?

I am writing a Queue data structure for python purely for learning purposes. here is my class. when I compare two Queue object for equality, I get error. I think the error pops up, because I dont compare for None in my __eq__ .but how can I check for None and return accordinly. in fact, I am using list under the hood and calling its __eq__, thinking it should take care as shown here, but it does not
>>> l=[1,2,3]
>>> l2=None
>>> l==l2
False
Here is my class:
#functools.total_ordering
class Queue(Abstractstruc,Iterator):
def __init__(self,value=[],**kwargs):
objecttype = kwargs.get("objecttype",object)
self.container=[]
self.__klass=objecttype().__class__.__name__
self.concat(value)
def add(self, data):
if (data.__class__.__name__==self.__klass or self.__klass=="object"):
self.container.append(data)
else:
raise Exception("wrong type being added")
def __add__(self,other):
return Queue(self.container + other.container)
def __iadd__(self,other):
for i in other.container:
self.add(i)
return self
def remove(self):
return self.container.pop(0)
def peek(self):
return self.container[0]
def __getitem__(self,index):
return self.container[index]
def __iter__(self):
return Iterator(self.container)
def concat(self,value):
for i in value:
self.add(i)
def __bool__(self):
return len(self.container)>0
def __len__(self):
return len(self.container)
def __deepcopy__(self,memo):
return Queue(copy.deepcopy(self.container,memo))
def __lt__(self,other):
return self.container.__lt__(other.container)
def __eq__(self, other):
return self.container.__eq__(other.container)
But when I try compare using the above class I get:
>>> from queue import Queue
>>> q = Queue([1,2,3])
>>> q
>>> print q
<Queue: [1, 2, 3]>
>>> q1 = None
>>> q==q1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "queue.py", line 65, in __eq__
return self.container.__eq__(other.container)
AttributeError: 'NoneType' object has no attribute 'container'
>>>
Your problem is how you are implementing __eq__.
Look at this code:
q = Queue([1,2,3])
q1 = None
q==q1
And lets rewrite it as the equivilent:
q = Queue([1,2,3])
q == None
Now, in Queue.__eq__ we have:
def __eq__(self, other):
return self.container.__eq__(other.container)
But other is None, which means the return statement is calling:
self.container.__eq__(None.container)
As your error rightly states:
'NoneType' object has no attribute 'container'
Because it doesn't! None doesn't have a container attribute.
So, the way to do it, depends on how you want to treat it. Now, obviously, a Queue object can't be None if its been defined, so:
return other is not None and self.container.__eq__(other.container)
Will lazily evaluate if other is None, and return False before evalauting the part of the expression after the and. Otherwise, it will perform the evaulation. However, you will get other issues if other is not of type Queue (or more correctly the other object doesn't have a container attribute), such as:
q = Queue([1,2,3])
q == 1
>>> AttributeError: 'int' object has no attribute 'container'
So... depending on your logic, and if a Queue can't be "equal" to other types (which is something only you can say), you can check for the correct type like so:
return other is not None and type(self) == type(other) and self.container.__eq__(other.container)
But... None is a NoneType, so it can never be of the same type as a Queue. So we can shorten it again to just:
return type(self) == type(other) and self.container.__eq__(other.container)
edit: As per mglisons comments:
This could be made more pythonic by using the regular equality statement:
return type(self) == type(other) and self.container == other.container
They have also raised a good point regarding the use of type in checking eaulity. If you are certain that Queue would never be subclassed (which is difficult to state). You could use exception handling to capture the AttributeError effectively, like so:
def __eq__(self, other):
try:
return self.container == other.container
except AttributeError:
return False # There is no 'container' attribute, so can't be equal
except:
raise # Another error occured, better pay it forward
The above may be considered a little overengineered, but is probably one of the better ways to approach this from a safety and resuability perspective.
Or a better, shorter approach (which I should have thought of initially) using hasattr is:
return hasattr(other, 'container') and self.container == other.container
Tell Python that you don't know how to compare against other types:
def __eq__(self, other):
if not isinstance(other, Queue):
return NotImplemented
return self.container.__eq__(other.container)
you might consider checking hasattr(other, 'container') instead of the isinstance, or catch the AttributeError.
But the important thing is that, unlike other answers recommend, you do not want to return False when other isn't a Queue. If you return NotImplemented, Python will give other a chance to check the equality; if you return False, it won't. Differentiate between the three possible answers to the question "are these objects equal": yes, no, and I don't know.
You'll want to do something similar in your __lt__, where the difference is even more apparent: if you return False from both __lt__ and __eq__, then the __gt__ inserted by total_ordering will return True - even though you can't do the comparison. If you return NotImplemented from both of them, it will also be NotImplemented.
you can do something like
def __eq__(self,other):
if other is None: return False
return self.container.__eq__(other.container)
You may also want to do something like
if not isinstance(other,Queue): return False

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.

Extending base classes in Python

I'm trying to extend some "base" classes in Python:
class xlist (list):
def len(self):
return len(self)
def add(self, *args):
self.extend(args)
return None
class xint (int):
def add(self, value):
self += value
return self
x = xlist([1,2,3])
print x.len() ## >>> 3 ok
print x ## >>> [1,2,3] ok
x.add (4, 5, 6)
print x ## >>> [1,2,3,4,5,6] ok
x = xint(10)
print x ## >>> 10 ok
x.add (2)
print x ## >>> 10 # Not ok (#1)
print type(x) ## >>> <class '__main__.xint'> ok
x += 5
print type(x) ## >>> <type 'int'> # Not ok (#2)
It works fine in the list case because the append method modifies the object "in place", without returning it. But in the int case, the add method doesn't modify the value of the external x variable. I suppose that's fine in the sense that self is a local variable in the add method of the class, but this is preventing me from modifying the initial value assigned to the instance of the class.
Is it possible to extend a class this way or should I define a class property with the base type and map all the needed methods to this property?
Your two xint examples don't work for two different reasons.
The first doesn't work because self += value is equivalent to self = self + value which just reassigns the local variable self to a different object (an integer) but doesn't change the original object. You can't really get this
>>> x = xint(10)
>>> x.add(2)
to work with a subclass of int since integers are immutable.
To get the second one to work you can define an __add__ method, like so:
class xint(int):
def __add__(self, value):
return xint(int.__add__(self, value))
>>> x = xint(10)
>>> type(x)
<class '__main__.xint'>
>>> x += 3
>>> x
13
>>> type(x)
<class '__main__.xint'>
int is a value type, so each time you do an assignment, (e.g. both instances of += above), it doesn't modify the object you have on the heap, but replaces the reference with one of the result of the right hand side of the assignment (i.e. an int)
list isn't a value type, so it isn't bound by the same rules.
this page has more details on the differences: The Python Language Reference - 3. Data model
IMO, yes, you should define a new class that keeps an int as an instance variable
i expanded you xlist class just a bit, made it so you could find all index points of a number making it so you can extend with multiple lists at once making it initialize and making it so you can iterate through it
class xlist:
def __init__(self,alist):
if type(alist)==type(' '):
self.alist = [int(i) for i in alist.split(' ')]
else:
self.alist = alist
def __iter__(self):
i = 0
while i<len(self.alist):
yield self.alist[i]
i+=1
def len(self):
return len(self.alist)
def add(self, *args):
if type(args[0])==type([1]):
if len(args)>1:
tmp = []
[tmp.extend(i) for i in args]
args = tmp
else:args = args[0]
if type(args)==type(''):args = [int(i) for i in args.split(' ')]
(self.alist).extend(args)
return None
def index(self,val):
gen = (i for i,x in enumerate(self.alist) if x == val)
return list(gen)
Ints are immutable and you can't modify them in place, so you should go with option #2 (because option #1 is impossible without some trickery).
I wrote an example of a mutable integer class that implements some basic methods from the list of operator methods. It can print properly, add, subtract, multiply, divide, sort, and compare equality.
If you want it to do everything an int can you'll have to implement more methods.
class MutablePartialInt:
def __init__(self, value):
self.value = value
def _do_relational_method(self, other, method_to_run):
func = getattr(self.value, method_to_run)
if type(other) is MutablePartialInt:
return func(other.value)
else:
return func(other)
def __add__(self, other):
return self._do_relational_method(other, "__add__")
def __sub__(self, other):
return self._do_relational_method(other, "__sub__")
def __mul__(self, other):
return self._do_relational_method(other, "__mul__")
def __truediv__(self, other):
return self._do_relational_method(other, "__truediv__")
def __floordiv__(self, other):
return self._do_relational_method(other, "__floordiv__")
def __eq__(self, other):
return self._do_relational_method(other, "__eq__")
def __neq__(self, other):
return self._do_relational_method(other, "__neq__")
def __lt__(self, other):
return self._do_relational_method(other, "__lt__")
def __gt__(self, other):
return self._do_relational_method(other, "__gt__")
def __str__(self):
return str(self.value)
def __repr__(self):
return self.__str__()

Categories

Resources