方法说明
在 Python 中,__getitem__
是一个特殊方法(也称为魔术方法或双下方法),它定义了序列类型(如列表、元组、字符串等)的索引行为。当你尝试通过索引访问一个对象的元素时,Python 会调用这个方法。
__getitem__
方法的作用是允许对象支持索引操作,例如使用方括号 []
来获取或设置元素。这个方法通常在自定义类中被重写,以便实现类似列表或字典的行为。
__getitem__
方法的基本语法如下:
python
def __getitem__(self, key):
# 实现索引逻辑
pass
这里的 self
是类的实例,key
是用于索引的键(对于序列类型通常是整数索引,对于字典类型则是键值)。
示例:自定义序列类
下面是一个简单的示例,展示了如何在自定义类中重写 __getitem__
方法来实现列表类似的行为:
python
class MySequence:
def __init__(self, elements):
self.elements = elements
def __getitem__(self, index):
if index < 0 or index >= len(self.elements):
raise IndexError("Index out of range")
return self.elements[index]
def __len__(self):
return len(self.elements)
# 创建一个 MySequence 实例
my_sequence = MySequence([1, 2, 3, 4, 5])
# 使用索引访问元素
print(my_sequence[0]) # 输出: 1
print(my_sequence[2]) # 输出: 3
# 尝试访问不存在的索引
try:
print(my_sequence[5])
except IndexError as e:
print(e) # 输出: Index out of range
在这个例子中,MySequence
类重写了 __getitem__
方法来支持索引访问。我们还重写了 __len__
方法来支持 len()
函数,这是一个好习惯,因为它允许用户知道序列的长度。
当你尝试访问 my_sequence
的元素时,例如 my_sequence[0]
,Python 会自动调用 __getitem__
方法,并传入索引 0
。如果索引有效,方法返回相应的元素;如果索引无效,方法可以抛出 IndexError
异常。
__getitem__
方法是实现序列协议的关键部分,它使得自定义对象可以像内置的序列类型一样被索引。
简单示例
python
class Demo:
def __init__(self):
self.arr = range(10)
def __len__(self):
return len(self.arr)
def __getitem__(self, index):
print("__getitem__ is call")
if 0 <= index < self.__len__():
return self.arr[index]
demo = Demo()
print(demo[0])
print(demo[3])
>>>
__getitem__ is call
0
__getitem__ is call
3
__getitem__
和迭代器
在 Python 中,for
循环实际上是通过迭代器(iterator)来实现的,而迭代器是支持一些特殊方法的对象。这些特殊方法包括:
__iter__(self)
:返回迭代器对象自身。这个方法使得对象成为一个可迭代对象(iterable),可以在for
循环中使用。__next__(self)
:返回迭代器的下一个值。当迭代器耗尽时,抛出StopIteration
异常,通知for
循环停止迭代。
在 for
循环中,Python 会自动调用可迭代对象的 __iter__()
方法来获取一个迭代器对象,然后通过调用迭代器对象的 __next__()
方法来获取值,直到迭代器耗尽为止。这样就实现了循环遍历可迭代对象的功能。
除了以上两个方法,还有一个相关的魔法方法是 __getitem__(self, key)
,它可以让对象支持通过索引访问元素,但这种方式不是 for
循环的底层实现方式,而是用于支持序列(sequence)类型的对象。
总结:
__getitem__
方法和迭代器没有任何关系。
__getitem__
实现的索引访问元素是随机的,迭代器实现的访问是顺序的。
iter方法
通过iter方法将一个实例变成可迭代对象,next取值的时候,优先通过__next__
取值,如果没有__next__
方法则通过__getitem__
方法取值。
存在__next__
方法
python
class Demo:
def __init__(self):
self.arr = range(10)
self.index = 0
def __len__(self):
return len(self.arr)
def __getitem__(self, index):
print("__getitem__ is call")
if 0 <= index < self.__len__():
return self.arr[index]
def __iter__(self):
return self
def __next__(self):
print("__next__ is call")
if self.index >= len(self.arr):
raise Exception("超出范围")
res = self.arr[self.index]
self.index += 1
return res
demo = Demo()
iter_demo = iter(demo)
print(next(iter_demo))
print(next(iter_demo))
>>>
__next__ is call
0
__next__ is call
1
没有__next__
方法则使用__getitem__
方法
python
class Demo:
def __init__(self):
self.arr = range(10)
self.index = 0
def __len__(self):
return len(self.arr)
def __getitem__(self, index):
print("__getitem__ is call")
if 0 <= index < self.__len__():
return self.arr[index]
# def __iter__(self):
# return self
#
# def __next__(self):
# print("__next__ is call")
# if self.index >= len(self.arr):
# raise Exception("超出范围")
#
# res = self.arr[self.index]
# self.index += 1
# return res
demo = Demo()
iter_demo = iter(demo)
print(next(iter_demo))
print(next(iter_demo))
>>>
__getitem__ is call
0
__getitem__ is call
1