检查对象是否在列表中(不是 "by value",而是通过 id)

Check if object is in list (not "by value", but by id)

考虑以下代码:

>>> class A(object):
...   def __init__(self, a):
...     self.a = a
...   def __eq__(self, other):
...     return self.a==other.a
... 
>>> a=A(1)
>>> b=A(1)
>>> c=A(2)

>>> a==b
True                   # because __eq__ says so
>>> a==c
False                  # because __eq__ says so
>>> a is b
False                  # because they're different objects

>>> l = [b,c]
>>> a in l
True                   # seems to use __eq__ under the hood

因此,in 似乎使用 __eq__ 来确定某物是否在容器中。

  1. 在哪里可以找到有关此行为的文档?
  2. 是否可以让 in 使用对象标识,a.k.a。 a in somelist 如果对象 asomelist 中,而不是其他比较等于 a 的对象?

使用 any() function 和生成器表达式:

any(o is a for o in l)

in 的行为记录在 Common Sequence Operators section:

x in s
True if an item of s is equal to x, else False

大胆强调我的。

如果您必须使用 in,请使用带有使用 is 的自定义 __eq__ 方法的包装器对象,或者在自定义 __contains__ method 使用的地方构建您自己的容器is 针对每个包含的元素进行测试。

包装器可能如下所示:

class IdentityWrapper(object):
    def __init__(self, ob):
        self.ob = ob
    def __eq__(self, other):
        return other is self.ob

演示:

>>> IdentityWrapper(a) in l
False
>>> IdentityWrapper(a) in (l + [a])
True

容器可以只使用上面概述的相同 any() 函数:

class IdentityList(list):
    def __contains__(self, other):
        return any(o is other for o in self)

演示:

>>> il = IdentityList(l)
>>> a in il
False
>>> a in IdentityList(l + [a])
True

如果您不想改变A行为,您可以为用过的容器准备薄包装纸。要改变 in 运算符的行为方式,魔术方法 __contains__ 需要被覆盖。引用文档:

Called to implement membership test operators. Should return true if item is in self, false otherwise. For mapping objects, this should consider the keys of the mapping rather than the values or the key-item pairs.

示例代码:

class A(object):
    def __init__(self, a):
        self.a = a

    def __eq__(self, other):
        return self.a == other.a


class IdentityList(list):
    def __contains__(self, obj):
        return any(o is obj for o in self)

a = A(1)
b = A(1)
c = A(2)
container = [b, c]
identity_container = IdentityList(container)
assert a in container  # not desired output (described in question)
assert a not in identity_container  # desired output