我正在阅读有关 Python 的 dunder 方法的内容。我学到的一件事是,如果一个类提供了__getitem__and的实现__len__,它可以在循环中使用for。
list查看、 、等内置类tuple,range我注意到它们都提供了一个实现,__iter__该实现返回相应类型的迭代器。我的理解是for循环使用这个迭代器来遍历元素。
__getitem__但是,对于提供但__len__不提供 的类,它如何工作__iter__?
作为示例,这里有一个Range模仿内置的类range:
class Range():
def __init__(self, start, stop=None, step=1):
if step == 0:
raise ValueError('step cannot be 0')
if stop is None:
start, stop = 0, start
self._length = max(0, (stop - start + step - 1) // step)
self._start = start
self._step = step
def __len__(self):
return self._length
def __getitem__(self, k):
if k < 0:
k = len(self) + k
if not 0 <= k < self._length:
raise IndexError('Index out of range')
return self._start + (k * self._step)
Run Code Online (Sandbox Code Playgroud)
用循环迭代它for:
In [21]: for elem in Range(5):
...: print(elem)
...:
0
1
2
3
4
Run Code Online (Sandbox Code Playgroud)
__iter__可迭代对象是一个定义 或的类__getitem__,不需要__len__。
__iter__实现与实现之间的区别__getitem__在于:
__iter__调用__next__从(又名迭代器)返回的对象__iter__,直到它到达StopIteration并且 for 循环停止的地方。然而__getitem__, 总是从零开始,每次迭代都会加一,直到达到IndexError,并且它会通过 来实现obj[idx]。
例如:
class GetItem:
def __getitem__(self, idx):
if idx == 10:
raise IndexError
return idx
for i in GetItem():
print(i)
Run Code Online (Sandbox Code Playgroud)
结果将是
0
1
2
...
9
Run Code Online (Sandbox Code Playgroud)
因为一旦索引达到 10,它就会上升IndexError并且循环停止。
__iter__另一方面,
0
1
2
...
9
Run Code Online (Sandbox Code Playgroud)
在这里,您需要自己跟踪状态,而在__getitem__它自己中,它更适合计数/索引等。