Explain Codes LogoExplain Codes Logo

Override Python's 'in' operator?

python
custom-membership-testing
python-classes
inheritance
Nikita BarsukovbyNikita Barsukov·Oct 15, 2024
TLDR

To override the 'in' operator, define the __contains__ method in your class. This method customizes the way Python checks for membership.

Here's a simple example:

class OddContainer: def __contains__(self, item): # If it's not odd, we don't want it! return item % 2 != 0 # Usage: print(1 in OddContainer()) # True, 1 is odd print(2 in OddContainer()) # False, 2 is as even as they come

Customizing membership testing with contains

Use the __contains__ method to customize membership testing according to your class's purpose. Here's an example where we use it to check if a custom object is in a collection:

class PortfolioItem: def __init__(self, ticker): self.ticker = ticker class Portfolio: def __init__(self, *args): # Best method to diversify? Have more tickers! self._holdings = list(args) def __contains__(self, item): # We don't care about the price, just the ticker! return any(item.ticker == obj.ticker for obj in self._holdings) # Usage: portfolio = Portfolio(PortfolioItem('AAPL'), PortfolioItem('GOOG'), PortfolioItem('TSLA')) print(PortfolioItem('AAPL') in portfolio) # True, AAPL joined the party print(PortfolioItem('MSFT') in portfolio) # False, MSFT wasn't invited

When contains isn't enough

If __contains__ isn't defined, Python defaults to __iter__, using it to check for membership. This is particularly useful for large data structures, but may not be the most efficient method for all data types.

Complex membership conditions

The __contains__ method can handle complex conditions beyond checking for simple membership:

class FancyContainer: def __init__(self): self._data = set() def __contains__(self, item): # Complex condition: Is the item a member of _data? Or does it meet some other condition? return (item in self._data or item_meets_some_other_condition(item)) def add(self, item): self._data.add(item)

Don't forget to test!

After defining __contains__, test various cases to confirm your in operator behaves correctly.

Keeping it Pythonic

When crafting your __contains__ method, remember the Zen of Python. Your __contains__ should leave little room for interpretation and should behave predictably.