dunder 方法 __getitem__ 和 __len__ 如何提供迭代?

Lit*_*ild 2 python for-loop

我正在阅读有关 Python 的 dunder 方法的内容。我学到的一件事是,如果一个类提供了__getitem__and的实现__len__,它可以在循环中使用for

list查看、 、等内置类tuplerange我注意到它们都提供了一个实现,__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)

Jon*_*609 5

__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__它自己中,它更适合计数/索引等。