Python __contains__() 语法
__contains__() 是 Python 的一个特殊方法(又称 “魔法方法” 或 “双下划线方法”),它用于定义当使用 in 运算符检查一个元素是否存在于对象中时,其行为是怎样的。
语法:
class MyContainer:
def __contains__(self, item):
# 检查 item 是否存在于容器中。必须返回 True 或 False
return item in self._elements
说明:
__contains__() 方法接收以下 2 个参数。
self(必选):约定俗成的名称,表示实例对象。item(必选):要检查的元素。
__contains__() 方法必须返回一个布尔值(True 或 False)。如果返回其他类型,Python 会抛出 TypeError 异常。
提示:
- 如果一个类实现了 __contains__() 方法,那么它的实例就支持使用 in 和 not in 运算符。
- 如果一个类没有实现 __contains__() 方法,那么 in 运算符会尝试通过遍历对象(即调用 __iter__() 或 __getitem__())来检查元素是否存在,这可能效率较低。因此,对于自定义容器类,如果需要频繁进行成员测试,强烈建议实现 __contains__()。
Python __contains__() 摘要
| 属于 | Python 魔法方法 |
|---|---|
| 使用频率 | 中 |
| 官方文档 | 查看 |
| 相关方法 | __iter__() |
Python __contains__() 示例
接下来,我们通过几个简单的例子来讲解一下 Python __contains__() 方法是如何使用的。
示例 1:对内置容器类型使用 in 运算符
# 列表
fruits = ['apple', 'banana', 'cherry']
print('banana' in fruits) # True
print('orange' in fruits) # False
# 字符串
s = 'lvyenet'
print('lvye' in s) # True
print('Lvye' in s) # False
# 字典(检查键)
d = {'name': 'Jack', 'age': 20}
print('name' in d) # True
print('city' in d) # False
# 集合
s = {10, 20, 30}
print(10 in s) # True
print(40 in s) # False
运行结果如下。
True
False
True
False
True
False
True
False
分析:
内置的容器类型(如列表、字符串、字典、集合等),其内部都实现了 __contains__() 方法(或通过其他方法支持 in 操作)。当我们使用 in 运算符时,Python 会自动调用内部的 __contains__() 来高效地检查元素是否存在。
示例 2:自定义容器实现 __contains__()
class Inventory:
def __init__(self, items):
self._items = set(items) # 使用集合作为内部存储,以获得高效的成员测试
def __contains__(self, item):
# 检查物品是否在库存中
return item in self._items
def add_item(self, item):
self._items.add(item)
def __repr__(self):
return f'Inventory({self._items})'
# 创建库存对象
inventory = Inventory(['铅笔', '尺子', '橡皮'])
# 使用 in 运算符检查
print('尺子' in inventory) # True
print('胶布' in inventory) # False
# 添加新物品后再次检查
inventory.add_item('胶布')
print('胶布' in inventory) # True
运行结果如下。
True
False
True
分析:
在 Inventory 类中,我们手动实现 __contains__() 方法,使得其实例支持 in 运算符。当执行 item in inventory 时,__contains__() 方法被调用,它会检查 item 是否存在于内部的 _items 集合中。
示例 3:没有 __contains__() 但可迭代的类
class MyIterable:
def __init__(self, data):
self._data = list(data)
def __iter__(self):
# 实现迭代协议
return iter(self._data)
# 注意:这里故意不实现 __contains__()
def __repr__(self):
return f'MyIterable({self._data})'
iterable = MyIterable([10, 20, 30, 40, 50])
# 使用 in 运算符检查 (会触发 __iter__() 遍历)
print(20 in iterable)
print(50 in iterable)
# 再次检查
print(30 in iterable)
运行结果如下。
True
True
True
分析:
如果一个类没有实现 __contains__() 方法,但它是可迭代的(即实现了 __iter__() 或 __getitem__()),那么 in 运算符会退而求其次,通过迭代对象来检查元素是否存在。不过需要注意的是,这种方式对于大型集合可能会效率非常低下。
示例 4:基于特定规则的 __contains__()
class NumberChecker:
def __init__(self, min_val, max_val):
self.min_val = min_val
self.max_val = max_val
def __contains__(self, number):
"""
检查一个数字是否在指定范围内。
这不依赖于内部存储的元素,而是基于规则。
"""
return self.min_val <= number <= self.max_val
def __repr__(self):
return f'NumberChecker(range=[{self.min_val}, {self.max_val}])'
# 创建检查器实例
checker1 = NumberChecker(10, 20)
# 检查数字
print(5 in checker1) # False
print(15 in checker1) # True
# 可以将范围设置为负数或小数
checker2 = NumberChecker(-0.5, 0.5)
print(0.2 in checker2) # True
print(-1.0 in checker2) # False
运行结果如下。
False
True
True
False
分析:
__contains__() 方法也可以用于实现基于自定义逻辑的成员测试,而不是简单地检查内部列表或集合。
在这个例子中,NumberChecker 类并没有一个内部的数字集合,而是根据 min_val 和 max_val 这两个属性定义了一个范围。
当对 NumberChecker 实例使用 in 运算符时,__contains__() 方法会执行一个逻辑检查,判断传入的 number 是否在这个预设的范围内。
