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

℡╲_俬逩灬. 提交于 2021-02-07 20:19:43

问题


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?

回答1:


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



回答2:


If you do not want to change A behaviour, you may prepare thin wrapper for used container. To change how in operator behaves, magic method __contains__ needs to get overridden. Quoting docs:

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.

Sample code:

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


来源:https://stackoverflow.com/questions/34498961/check-if-object-is-in-list-not-by-value-but-by-id

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!