ran*_*ure 6 python equality contains list set
我正在使用python 2.7
考虑以下代码片段(示例是设计的):
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)
Run Code Online (Sandbox Code Playgroud)
这个输出是出乎意料的(对我来说,至少):
[08:02 PM toolscripts]$ python test.py
True
False
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,给定日期在schedule_set我已经覆盖__hash__和__eq__函数中找到.
根据我的理解,in运算符将检查集合的哈希和相等性,但对于列表,它将简单地迭代列表中的项目并检查相等性.
那么这里发生了什么?为什么in列表中的第二次测试schedule_list失败了?
我是否必须覆盖列表的其他功能?
问题是比较是调用__eq__与您正在寻找的功能相反的功能.__eq__定义的方法在您有a ScheduleData() == datetime.date()但in操作符以相反的顺序执行比较时起作用,datetime.date() == ScheduleData()而不是调用您定义的__eq__.只有作为左手边的类才会__eq__被调用.
python 2而不是3中出现此问题的原因与datetime.date.__eq__std库中的定义有关.以下面两个类为例:
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
Run Code Online (Sandbox Code Playgroud)
运行此代码B.__eq__在Python 2和Python 3下打印.该B对象用作lhs,就像datetime.date在Python 2中使用对象一样.但是,如果我重新定义B.__eq__为类似于Python 3的定义datetime.date.__eq__:
class B(object):
def __eq__(self, other):
print ('First B.__eq__')
if isinstance(self, other.__class__):
print ('B.__eq__')
return NotImplemented
Run Code Online (Sandbox Code Playgroud)
然后:
First B.__eq__
A.__eq__
Run Code Online (Sandbox Code Playgroud)
在Python 2和3下打印.返回NotImplemented导致检查反转的参数.
timetuple 在你的课堂上使用将解决这个问题,就像@TimPeters所说的那样(有趣的怪癖我不知道),虽然看起来它不一定是一个功能
class ScheduleData:
timetuple = None
Run Code Online (Sandbox Code Playgroud)
除了你已经拥有的东西,你还需要它.
@RyanHaining是对的.对于一个真正奇怪的解决方法,请将此方法添加到您的类:
def timetuple(self):
return None
Run Code Online (Sandbox Code Playgroud)
然后你的程序将打印True两次.造成这种情况的原因是,与Python 2中不幸的比较历史太过松散有关.该timetuple()解决方法是在文档的这一部分主要解释:
注意为了停止比较以回退到比较对象地址的默认方案,如果另一个比较不是datetime对象,则datetime比较通常会引发TypeError.但是,如果另一个comparand具有timetuple()属性,则返回NotImplemented.这个钩子为其他类型的日期对象提供了实现混合类型比较的机会.如果不是,当将datetime对象与不同类型的对象进行比较时,除非比较为==或!=,否则会引发TypeError.后一种情况分别返回False或True.
datetime是第一个添加到Python中的类型之一,试图提供不那么令人惊讶的比较行为.但是,在Python 3之前,它不会变得"非常干净".