它定义了如何通过索引来获取对象的成员。当我们使用方括号语法来访问一个对象的元素时,Python 解释器实际上是在调用该对象的 __getitem__ 方法
__getitem__ 方法的基本签名如下:
def __getitem__(self, key):
pass
这里的 key 参数可以是多种类型,具体取决于对象的设计。常见的类型包括整数(对于序列类型的索引)、切片对象(对于支持切片的操作)、或者任意可哈希的类型(对于字典或者其他映射类型)。
为了更好地理解 __getitem__ 的工作原理,让我们来看几个具体的实现案例:
1. 实现序列类型
如果我们想创建一个类似列表的序列类型,我们可以让 __getitem__ 接受一个整数索引,并返回该位置上的元素:
class MyList:
def __init__(self, iterable):
self.data = iterable
def __getitem__(self, index):
if index < 0 or index >= len(self.data):
raise IndexError("Index out of range")
return self.data[index]
mylist = MyList([1, 2, 3, 4, 5])
print(mylist[2])# 输出:3
print(mylist[5])# 输出:raise IndexError("Index out of range")
2. 支持切片操作
如果我们的类还希望支持切片操作,那么 __getitem__ 方法就需要处理 slice 对象:
class MyList1:
def __init__(self, iterable):
self.data = iterable
def __getitem__(self, key):
if isinstance(key, int):
if key < 0 or key >= len(self.data):
raise IndexError("Index out of range")
return self.data[key]
elif isinstance(key, slice): # slice类型是切片类型
return [self.data[i] for i in range(*key.indices(len(self.data)))]
# 使用 `slice.indices()` 方法可以帮助确保即使在提供了不完整的切片参数时也能正确地进行切片操作
# slice.indices() 方法返回一个元组,包含三个元素:起始索引、结束索引和步长。
# slice.indices() 传入的参数即序列的长度,用于计算切片的边界。
else:
raise TypeError("输入的key类型不正确,invalid key type")
mylist1 = MyList1([1, 2, 3, 4, 5])
print(mylist1[2]) # 输出:3
print(mylist1[2:4]) # 输出:[3, 4]
s = slice(1, 5, 2) # slice(start,stop,step) 起始、结束和步长
print(mylist1[s]) # 输出:[2, 4]
3. 映射类型
对于映射类型(如字典), 方法将接受一个键并返回对应的值:__getitem__
class MyDict:
def __init__(self, mapping=None):
self.data = dict(mapping or {})
def __getitem__(self, key):
if key in self.data:
return self.data[key]
else:
raise KeyError(key)
mydict=MyDict({'a': 1, 'b': 2})
print(mydict['a']) # 输出:1
print(mydict['c']) # 输出:raise KeyError('c')
可迭代性
当一个类实现了__getitem__ 方法后,它就可以通过索引来访问其内部的数据。 此外,这样的类还可以被视为可迭代对象,这意味着它可以用于循环和其他需要迭代的地方。 不过,为了让 Python 知道如何迭代这个对象,通常还需要实现__iter__方法或者__next__方法来提供迭代器。
下面是一个简单的例子,展示了一个名为Counter的类,它可以被用来计数从0开始的整数,并且可以被迭代:
class Counter:
def __init__(self, max_count=10):
self.max_count = max_count
self.reset()
def reset(self):
self.current = 0
def __getitem__(self, index):
if not 0 <= index < self.max_count:
raise IndexError
return index
def __iter__(self):
return self
def __next__(self):
if self.current >= self.max_count:
raise StopIteration
else:
result = self.current
self.current += 1
return result
# 使用示例
counter = Counter(5)
print(counter[1]) # 输出 1
for number in counter:
print(number) # 将会依次打印 0, 1, 2, 3, 4
在这个例子中,Counter类有一个max_count属性,定义了计数的最大值。 __getitem__方法允许我们按照索引访问计数器的值。__iter__方法使对象本身成为一个迭代器。 __next__方法在每次调用时返回下一个数,直到达max_count到为止,之后它将抛出StopIteration异常,告诉Python迭代应该停止。这样,Counter类就既可以通过索引访问,也可以作为可迭代对象使用了。
总结来说, __getitem__方法是实现基于索引或键的数据访问的核心,而支持迭代则需要进一步实现迭代器协议相关的魔术方法。