Python
in operator
operator overloading
custom methods
programming

Override Python's 'in' operator?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Yes, Python lets you customize the behavior of the in operator for your own classes. The direct hook is __contains__, and if you do not implement it, Python falls back to iteration or indexed access depending on what the object supports.

Implement __contains__

The cleanest way to override membership checks is to define __contains__ on your class:

python
1class WordBag:
2    def __init__(self, words):
3        self._words = {word.lower() for word in words}
4
5    def __contains__(self, item):
6        return item.lower() in self._words
7
8
9bag = WordBag(["Apple", "Banana", "Cherry"])
10
11print("banana" in bag)
12print("pear" in bag)

This prints:

text
True
False

Here the membership test is case-insensitive because __contains__ defines the rule that in should use.

What Python Does If __contains__ Is Missing

If __contains__ is not defined, Python tries other protocols:

  • iterate with __iter__
  • if iteration is unavailable, fall back to index access through __getitem__

That means this class still works with in even without __contains__:

python
1class NumberRange:
2    def __init__(self, start, end):
3        self.start = start
4        self.end = end
5
6    def __iter__(self):
7        return iter(range(self.start, self.end))
8
9
10numbers = NumberRange(1, 5)
11print(3 in numbers)

Python checks membership by iterating over the values until it finds a match or reaches the end.

This fallback is useful, but it is not always efficient. If your object can answer membership directly, __contains__ is usually the better choice.

Use __contains__ to Express Domain Logic

Custom membership logic can be more meaningful than a plain value lookup. For example, a range type can implement mathematical containment without building all values:

python
1class ClosedRange:
2    def __init__(self, start, end):
3        self.start = start
4        self.end = end
5
6    def __contains__(self, item):
7        return self.start <= item <= self.end
8
9
10r = ClosedRange(10, 20)
11
12print(15 in r)
13print(25 in r)

This is clearer and faster than materializing every number in the range just to support in.

The same idea works for:

  • geometric regions
  • permission collections
  • case-insensitive containers
  • custom lookup rules based on ids or aliases

The operator stays readable, but the class owns the rule.

Keep the Semantics Predictable

Overriding in is powerful, but the behavior should still feel natural. If item in container means something surprising, the class becomes harder to use correctly.

Good membership semantics usually answer a yes-or-no question about whether the container includes the item according to the class's purpose. They should not mutate state, trigger network calls unexpectedly, or perform expensive work that readers would not anticipate from a membership check.

If the check is expensive or ambiguous, an explicit method such as contains_alias() or matches_rule() may be a better API than overloading in.

Common Pitfalls

The biggest mistake is forgetting that __contains__ should return a boolean-like result. Returning unrelated values makes the class confusing and can break expectations in calling code.

Another issue is implementing membership through slow linear scans when the object already has a faster internal structure, such as a set or dictionary. If performance matters, use the structure directly inside __contains__.

Be careful with normalization logic. If your class lowercases stored values but not the input item, membership results may become inconsistent.

Finally, do not overload in with semantics that are too clever. Readability is the whole reason the operator is useful.

Summary

  • Override Python's in operator by defining __contains__.
  • If __contains__ is missing, Python falls back to iteration or indexed access.
  • Use __contains__ when the object can answer membership directly and clearly.
  • Keep the behavior predictable and aligned with the container's purpose.
  • Prefer explicit methods when membership would otherwise mean something surprising.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.