问题背景:
序列是Python中最古老和最基本的概念之一。经典示例包括列表(list)、字符串(str)、元组(tuple)。为了与序列进行交互,定义了一个特殊的协议:需要实现__getitem__和__len__方法。这使得对象可以表现得像"序列",支持索引、切片、循环及某些标准函数。
问题:
如果没有正确实现这些方法,用户自定义类将无法进行索引操作、使用for循环或如len()这样的函数。初学者往往只实现其中一个方法,不处理异常情况,不支持切片(slice),导致出现不正确或意外的行为。
解决方案:
需要实现__getitem__(self, key)方法以支持索引和切片,以及__len__(self)方法以支持len()函数和正确的可迭代性。为了支持切片,需要在__getitem__中区分key的类型并正确处理slice对象。
示例代码:
def is_even(n): return n % 2 == 0 class EvenSequence: def __init__(self, size): self.size = size def __getitem__(self, index): if isinstance(index, slice): return [x for x in range(self.size)[index] if is_even(x)] if index < 0 or index >= self.size: raise IndexError('索引超出范围') return index if is_even(index) else None def __len__(self): return self.size
关键特性:
只实现__getitem__,对象能否作为序列工作?
部分可以。如果只实现__getitem__,则可以通过for进行迭代和索引元素,但len()将无法使用。
示例代码:
class SeqOnlyGetitem: def __getitem__(self, index): if index >= 10: raise IndexError return index * 2 s = SeqOnlyGetitem() for x in s: print(x) # 工作正常(可迭代) # print(len(s)) # 错误 TypeError
如何处理负索引,若不考虑其后果会怎样?
负索引是Python序列的关键特性。如果不处理,对象的行为会让用户感到意外。
class NegIndex: def __init__(self, data): self.data = data def __getitem__(self, index): if index < 0: index += len(self.data) return self.data[index]
在自己的Sequence类中需要实现__contains__或__iter__吗?
不一定。如果实现了__getitem__和__len__,则in操作会正常工作,for会使用它们进行迭代。通常不需要直接实现这些方法,但可能会提高性能。
开发者仅为自己的类实现__getitem__,仅支持正索引。模块通过了一部分单元测试,但在实际使用中,试图获取不存在的索引或负索引时出现了问题。
优点:
缺点:
团队实现了两个方法(getitem__和__len),考虑了切片、负索引,抛出了正确的异常。最终类在Python中的所有标准场景下工作良好。
优点:
缺点: