编程中级 Python 开发者

'in' 运算符如何在 Python 中对用户对象工作?为了使表达式 'x in your_obj' 有效,需要在类中实现什么?如何避免性能问题和意外错误?

用 Hintsage AI 助手通过面试

答案。

在 Python 中,in 运算符用于检查元素是否在集合中。为了支持 x in your_obj 的结构,需要实现 方法 __contains__。如果没有此方法,解释器将尝试通过 __iter____getitem__ 来迭代对象,但行为和效率可能有所不同。

示例:

class MyBag: def __init__(self, items): self.items = items def __contains__(self, value): return value in self.items bag = MyBag([1,2,3]) print(2 in bag) # True print(5 in bag) # False

如果只实现了 __iter__(甚至只是 __getitem__),则 in 将工作,但效率较低,有时表现得与预期完全不同。

注意:如果集合庞大,而检查是以简单的方式实现的(例如,通过遍历整个列表),则可能会出现性能问题。为了快速检查,可以使用集合等。

骗人的问题。

仅实现 __iter__ 或仅实现 __getitem__ 是否足以确保 in 运算符的正常工作?行为将如何变化?

答案:

  • 如果没有 __contains__,Python 将尝试使用 __iter__(如果存在)或 __getitem__(从索引 0 开始,直到引发 IndexError)来遍历元素。
  • 这种行为效率较低,并且可能会导致无限循环或异常,如果方法的实现有拼写错误。

示例:

class Weird: def __getitem__(self, idx): if idx < 3: return idx raise IndexError w = Weird() print(2 in w) # True print(5 in w) # False

由于不清楚该主题的细微差别而导致的真实错误示例。


故事

在一个项目中,自定义容器用于存储实体,仅重写了 __iter__,忘记实现 __contains__in 运算符开始变得不仅慢(对于大型集合,延迟明显),而且在迭代器错误地抛出非 StopIteration 类型的异常时发生神秘的崩溃。


故事

对于一个按索引 "即时" 计算元素的类,开发者仅实现了 __getitem__。当尝试使用较大的 x 检查 x in obj 时,会出现长时间循环,甚至内存溢出 — 因为 in 会按递增顺序检查所有索引,直到遇到 IndexError。


故事

在一个项目中实现了自定义字典,仅依赖于 __iter__ 来处理 in。这导致对于 100,000 个键的搜索需要数秒,而标准的 dict(其 __contains__ 实现有效)只需毫秒。