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.
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
?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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With