【问题标题】:python set contains vs. list containspython set contains vs. list contains
【发布时间】:2013-11-26 04:04:00
【问题描述】:

我正在使用 python 2.7

考虑下面的sn-p代码(例子是人为的):

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)

这个输出是出乎意料的(至少对我来说):

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

在第一种情况下,给定的日期在 schedule_set 中找到,因为我已经覆盖了 __hash____eq__ 函数。

据我了解,in 运算符将检查集合的哈希和相等性,但对于列表,它只会遍历列表中的项目并检查相等性。

那么这里发生了什么?为什么我对列表schedule_list 上的in 的第二次测试失败了?

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

【问题讨论】:

  • 为我工作.. Python 3.3 我想知道为什么。确认,它不适用于 Python 2.7
  • 也尝试添加__neq__
  • @tcaswell,谢谢我已经尝试过并且应该提到它,但它不起作用 - 那些 __ne____neq____eq__ 函数似乎没有得到在第二种情况下调用。
  • 比较 datetime.date(2010, 8, 8) == schedule_list[1]schedule_list[1] == datetime.date(2010, 8,8)。你得到False,然后是True
  • 错误。我相信问题出在课堂上。

标签: python list set contains equality


【解决方案1】:

问题是比较调用与您正在寻找的相反的__eq__ 函数。当您有 ScheduleData() == datetime.date()in 运算符以相反的顺序执行比较时,__eq__ 定义的方法有效,datetime.date() == ScheduleData() 没有调用您定义的 __eq__。只有充当左侧的类才会调用其__eq__

这个问题出现在python 2而不是3的原因与std库中datetime.date.__eq__的定义有关。以以下两个类为例:

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

运行此代码会在 Python 2 和 Python 3 下打印 B.__eq__B 对象用作 lhs,就像您的 datetime.date 对象在 Python 2 中使用一样。但是,如果我重新定义 B.__eq__类似于 datetime.date.__eq__ 的 Python 3 定义:

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

然后:

First B.__eq__
A.__eq__ 

在 Python 2 和 3 下都打印出来。NotImplemented 的返回会导致参数反转。

在课堂上使用timetuple 将解决这个问题,正如@TimPeters 所说(我不知道的有趣怪癖),尽管它似乎不需要是一个函数

class ScheduleData:
    timetuple = None

除了你已经拥有的东西之外,你还需要它。

【讨论】:

  • 我要补充一点,这种情况下的行为取决于使用 datetime.date 作为比较类型。例如,如果您传入一个 int 或字符串作为“日期”值,则同一个类可以正常工作。所以问题还在于datetime.date 没有与内置类型相同的自动双向比较行为。
  • @BrenBarn 我也怀疑这一点,我现在正在查看库代码
【解决方案2】:

@RyanHaining 是正确的。对于一个真正奇怪的解决方法,将此方法添加到您的类中:

def timetuple(self):
    return None

然后你的程序会打印两次True。造成这种情况的原因是与 Python 2 中的不幸比较历史太松散有关。 timetuple() 解决方法主要在这部分文档中进行了解释:

注意为了防止比较回落到 比较对象地址的默认方案,日期时间 如果另一个比较对象,比较通常会引发 TypeError 也不是日期时间对象。但是,NotImplemented 是 如果另一个比较有 timetuple() 则返回 属性。这个钩子为其他类型的日期对象提供了一个 实现混合类型比较的机会。如果不, 当一个日期时间对象与一个对象进行比较时 不同的类型,除非比较,否则会引发 TypeError 是 == 或 !=。后一种情况返回 False 或 True, 分别。

datetime 是最早添加到 Python 中的类型之一,它试图提供不那么令人惊讶的比较行为。但是,在 Python 3 之前它不能变得“真正干净”。

【讨论】:

  • 感谢 tim,我接受了 ryan 的回答,但你的回答同样有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-22
  • 2011-12-13
  • 1970-01-01
  • 2012-04-01
  • 2011-02-05
  • 2013-08-06
  • 1970-01-01
相关资源
最近更新 更多