python set contains包含列表

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失败了?

我是否必须覆盖列表的其他功能?

Rya*_*ing 9

问题是比较是调用__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)

除了你已经拥有的东西,你还需要它.


Tim*_*ers 6

@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之前,它不会变得"非常干净".