The python suite contains vs. list contains

I am using python 2.7

consider the following code fragment (an example is contrived):

import datetime class ScheduleData: def __init__(self, date): self.date = date def __eq__(self, other): try: return self.date == other.date except AttributeError as e: return self.date == other def __hash__(self): return hash(self.date) schedule_set = set() schedule_set.add(ScheduleData(datetime.date(2010, 8, 7))) schedule_set.add(ScheduleData(datetime.date(2010, 8, 8))) schedule_set.add(ScheduleData(datetime.date(2010, 8, 9))) print (datetime.date(2010, 8, 8) in schedule_set) schedule_list = list(schedule_set) print (datetime.date(2010, 8, 8) in schedule_list) 

the output from this is unexpected (at least for me):

 [08:02 PM toolscripts]$ python test.py True False 

in the first case, the specified date was found in schedule_set , because I redefined the functions __hash__ and __eq__ .

from my understanding, the in operator will check the hash and equality for sets, but for lists it just iterates over the elements in the list and checks for equality.

so what's going on here? why is my second test for in in the schedule_list not working?

Do I need to override any other function for lists?

+6
source share
2 answers

The problem is that the comparison calls the __eq__ function, the opposite of what you are looking for. A __eq__ method works if you have ScheduleData() == datetime.date() , but the in operator performs the comparison in reverse order, datetime.date() == ScheduleData() , which does not call your specific __eq__ . Only the class acting as the left side will have its __eq__ .

The reason this problem occurs in python 2 rather than 3 is due to the definition of datetime.date.__eq__ in the std library. Take, for example, the following two classes:

 class A(object): def __eq__(self, other): print ('A.__eq__') return False class B(object): def __eq__(self, other): print ('B.__eq__') items = [A()] B() in items 

Running this code prints B.__eq__ under both Python 2 and Python 3. Object B used as lhs, just like your datetime.date object is used in Python 2. However, if I redefine B.__eq__ to resemble Python 3 defintion datetime.date.__eq__ :

 class B(object): def __eq__(self, other): print ('First B.__eq__') if isinstance(self, other.__class__): print ('B.__eq__') return NotImplemented 

Then:

 First B.__eq__ A.__eq__ 

printed under both Python 2 and 3. Returning NotImplemented causes a check with the arguments changed.

Using timetuple in your class, this problem will be fixed, as pointed out by @TimPeters (an interesting workaround that I did not know about), although it seems that this should not be a function

 class ScheduleData: timetuple = None 

- that’s all you need in addition to what you already have.

+8
source

@RyanHaining is correct. For a truly weird workaround, add this method to your class:

 def timetuple(self): return None 

Then your program will print True twice. The reasons for this are due to the poor history of comparisons in Python 2, which are too weak. The timetuple() workaround is mainly explained in this part of the docs:

Note. To stop the comparison from returning to the standard scheme for comparing the addresses of objects, date and time, the comparison usually raises a TypeError if the other comparison and isnt also a datetime object. However, NotImplemented is returned if another comparison has a timetuple () attribute. This hook gives other types of date objects a chance to implement mixed comparisons. If not, when the datetime object is compared with an object of a different type, a TypeError is raised if the comparison is == or! =. Recent cases return False or True, respectively.

datetime was one of the first types added in Python to try to offer less surprising comparison behavior. But it could not become "really clean" until Python 3.

+6
source

Source: https://habr.com/ru/post/958823/