问题
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.
- Where can one find documentation on this behavior?
- Is it possible to make
in
use object identity, a.k.a.a in somelist
if the objecta
is insomelist
, and not some other object that compares equal toa
?
回答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, elseFalse
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