为何使用in检查时__getitem__接收的key变为0?
问题:实现
__getitem__后执行key in obj时传入的key变为0 问题重现
实现了如下类:
class Mapper: def __getitem__(self, key): print(f'Retrieving {key!r}') if key == 'a': return 1 else: raise KeyError('This only contains a')
单独用obj[key]可以正常工作:
>>> mapper['a'] Retrieving 'a' 1
但执行'a' in mapper时,却出现异常,且传入__getitem__的key变成了0:
>>> 'a' in mapper Retrieving 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 7, in __getitem__ KeyError: 'This only contains a'
原因分析
你误解了Python中in操作的默认逻辑:
- 当类没有实现
__contains__方法(你找的__hasitem__是错误的方法名,正确的是__contains__)时,Python会将对象视为序列类型,而非映射类型。 - 此时
in操作会尝试通过__getitem__从索引0开始逐个取值,直到抛出IndexError才会判定元素不存在——而不是直接用目标key去调用__getitem__并捕获KeyError。这就是为什么会传入整数0的原因。
解决方案
方案1:实现__contains__方法(推荐)
直接定义__contains__方法来判断key是否存在,这是映射类型的标准实现方式:
class Mapper: def __getitem__(self, key): print(f'Retrieving {key!r}') if key == 'a': return 1 else: raise KeyError('This only contains a') def __contains__(self, key): # 直接判断key是否在允许的范围内 return key == 'a'
测试验证:
>>> mapper = Mapper() >>> 'a' in mapper True >>> 'b' in mapper False >>> mapper['a'] Retrieving 'a' 1
方案2:在__getitem__中处理序列遍历的情况(不推荐)
如果不想实现__contains__,可以在__getitem__中判断如果是整数索引,直接抛出IndexError,让Python停止序列遍历:
class Mapper: def __getitem__(self, key): print(f'Retrieving {key!r}') # 拦截整数索引,触发序列遍历终止逻辑 if isinstance(key, int): raise IndexError if key == 'a': return 1 else: raise KeyError('This only contains a')
这种方式可以让'a' in mapper返回False(因为遍历0时抛出IndexError),但不符合映射类型的设计逻辑,不推荐使用。
内容的提问来源于stack exchange,提问作者Nick S
相关产品推荐
相关产品推荐

