Python __contains__ Method

Complete guide to Python's __contains__ method covering membership testing, custom containers, and operator overloading.

Python __contains__ Method

Python contains Method

Last modified April 8, 2025

This comprehensive guide explores Python’s contains method, the special method that implements membership test operations. We’ll cover basic usage, custom containers, performance considerations, and practical examples.

Basic Definitions

The contains method implements the membership test operators (in and not in). It should return True if the item is in the container, False otherwise.

Key characteristics: it takes one argument (the item to test), can be defined in any class, and is automatically invoked when using the in operator. It enables custom containment logic for user-defined objects.

Basic contains Implementation

Here’s a simple implementation showing how contains works with the in operator. This example checks for membership in a list.

basic_contains.py

class MyContainer: def init(self, items): self.items = items

def __contains__(self, item):
    return item in self.items

container = MyContainer([1, 2, 3, 4, 5]) print(3 in container) # True print(6 in container) # False

This example demonstrates the basic pattern. The contains method delegates to the underlying list’s containment check. The in operator automatically calls this method.

The method returns a boolean value indicating whether the item exists in the container. This simple implementation mirrors Python’s built-in list behavior.

Custom Membership Logic

contains allows implementing custom membership test logic that goes beyond simple value checking. Here we check for even numbers.

custom_logic.py

class EvenNumbers: def contains(self, num): return isinstance(num, int) and num % 2 == 0

evens = EvenNumbers() print(4 in evens) # True print(5 in evens) # False print(“a” in evens) # False

This implementation doesn’t store numbers but defines what constitutes membership mathematically. It checks if the input is an integer and even.

The example shows how contains can implement abstract containment rules without actual storage. This pattern is useful for virtual containers.

Case-Insensitive String Container

This example demonstrates a string container that performs case-insensitive membership tests, useful for case normalization scenarios.

case_insensitive.py

class CaseInsensitiveContainer: def init(self, items): self.items = [item.lower() for item in items]

def __contains__(self, item):
    return item.lower() in self.items

fruits = CaseInsensitiveContainer([‘Apple’, ‘Banana’, ‘Orange’]) print(‘apple’ in fruits) # True print(‘BANANA’ in fruits) # True print(‘pear’ in fruits) # False

The container stores lowercase versions of all items and converts the search term to lowercase before checking. This ensures case doesn’t affect membership tests.

This pattern is useful when you need case-insensitive lookups but want to preserve the original casing of stored items for display purposes.

Range-Based Containment

This example implements a container that checks if a number falls within any of its stored ranges, demonstrating complex containment logic.

range_container.py

class RangeContainer: def init(self, ranges): self.ranges = ranges

def __contains__(self, num):
    return any(start <= num <= end for start, end in self.ranges)

ranges = RangeContainer([(1, 5), (10, 15), (20, 25)]) print(3 in ranges) # True print(8 in ranges) # False print(22 in ranges) # True

The container stores tuples representing ranges. The contains method checks if the number falls within any of these ranges using a generator expression with any().

This implementation efficiently handles multiple discontinuous ranges without storing every possible value, making it memory-efficient for large ranges.

Performance Optimization with contains

This example shows how contains can optimize membership tests by using a set for O(1) lookups instead of O(n) list searches.

optimized_contains.py

class OptimizedContainer: def init(self, items): self.items = list(items) self.items_set = set(items)

def __contains__(self, item):
    return item in self.items_set

large_data = OptimizedContainer(range(1000000)) print(999999 in large_data) # Fast lookup print(-1 in large_data) # Fast negative result

The class maintains both a list (for ordered access) and a set (for fast membership tests). The contains method uses the set for constant-time lookups.

This pattern is valuable when you need both ordered iteration and frequent membership tests. The memory overhead is justified by the performance gain.

Best Practices

  • Return boolean values: Should return True or False, not other types

  • Consider performance: Optimize for expected usage patterns

  • Maintain consistency: Behavior should match other container methods

  • Handle edge cases: Decide how to handle None, NaN, or other special values

  • Document behavior: Clearly specify what constitutes membership

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.

ad ad