Search code examples
pythoncomparison-operators

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


Consider the following code:

>>> 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

So, in seems to use __eq__ to determine whether or not something is in a container.

  1. Where can one find documentation on this behavior?
  2. Is it possible to make in use object identity, a.k.a. a in somelist if the object a is in somelist, and not some other object that compares equal to a?

Solution

  • Use the any() function and a generator expression:

    any(o is a for o in l)
    

    The behaviour of in is documented in the Common Sequence Operators section:

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

    Bold emphasis mine.

    If you must use in, use a wrapper object with a custom __eq__ method that uses is, or build your own container where a custom __contains__ method uses is to test against each contained element.

    The wrapper could look like this:

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

    Demo:

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

    The container could just use the same any() function outlined above:

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

    Demo:

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