class Membership:
def __init__(self, allowed):
self.allowed = allowed
def __contains__(self, item):
return item in self.allowed
m = Membership(['a', 'b'])
assert 'a' in m
assert 'z' not in m
class Both:
def __iter__(self):
return iter([1, 2, 3])
def __contains__(self, item):
return item == 99
b = Both()
assert 99 in b
assert 1 not in b
assert list(b) == [1, 2, 3]
assert [x for x in b] == [1, 2, 3]
class Truthy:
def __contains__(self, item):
return item
assert 5 in Truthy()
assert 0 not in Truthy()
assert [0] in Truthy()
assert [] not in Truthy()
class NoneContains:
def __contains__(self, item):
return None
assert 1 not in NoneContains()
assert isinstance(1 in NoneContains(), bool)
class EmptyContainer:
def __contains__(self, item):
return []
assert 1 not in EmptyContainer()
class OptOut:
__contains__ = None
try:
1 in OptOut()
assert False, 'expected TypeError for an opted-out __contains__'
except TypeError as e:
assert str(e) == "'OptOut' object is not a container"
class OptOutIterable:
def __iter__(self):
return iter([1, 2])
__contains__ = None
try:
1 in OptOutIterable()
assert False, 'expected TypeError rather than a fallback to iteration'
except TypeError as e:
assert str(e) == "'OptOutIterable' object is not a container"
try:
1 not in OptOutIterable()
assert False, 'expected TypeError from `not in` as well'
except TypeError as e:
assert str(e) == "'OptOutIterable' object is not a container"
assert list(OptOutIterable()) == [1, 2]
class Recorder:
def __init__(self):
self.seen = []
def __contains__(self, item):
self.seen.append(item)
return False
r = Recorder()
assert 'x' not in r
assert 'y' not in r
assert r.seen == ['x', 'y']
class IterOnly:
def __iter__(self):
return iter(['p', 'q'])
assert 'p' in IterOnly()
assert 'z' not in IterOnly()
class Boom:
def __contains__(self, item):
raise ValueError('nope')
try:
1 in Boom()
assert False, 'expected ValueError from __contains__'
except ValueError as e:
assert str(e) == 'nope'
try:
1 not in Boom()
assert False, 'expected ValueError from __contains__ via not in'
except ValueError as e:
assert str(e) == 'nope'
class NotCallable:
__contains__ = 42
try:
1 in NotCallable()
assert False, 'expected TypeError for non-callable __contains__'
except TypeError as e:
assert str(e) == "'int' object is not callable"
class InstanceOnly:
def __init__(self):
self.__contains__ = lambda item: True
try:
1 in InstanceOnly()
assert False, 'expected TypeError for instance-only __contains__'
except TypeError as e:
assert str(e) == "argument of type 'InstanceOnly' is not a container or iterable"
class Nested:
def __init__(self, inner):
self.inner = inner
def __contains__(self, item):
return item in self.inner
assert 1 in Nested(Nested([1, 2]))
assert 3 not in Nested(Nested([1, 2]))