Python中的可迭代对象的for循环是如何工作的?

12 投票
7 回答
14650 浏览
提问于 2025-04-15 13:40

我对Python的for循环语法有点困惑。比如下面这个代码示例:

for party in feed.entry:
    print(party.location.address.text)

在这里,for party in feed.entry到底是什么意思呢?因为feed.entry是一个可以被遍历的对象(也就是iterable)。当这个循环运行时,具体发生了什么?能不能一步一步解释一下?


这个问题主要是关于循环语法在技术层面上的实现。如果你想要初学者级别的语法解释,可以查看理解Python中的for循环

如果你在调试时遇到常见问题,比如在像for x in y:这样的循环中错误地尝试使用y[x],可以参考为什么在"for i in ar"中使用"ar[i]"会出现IndexError(或TypeError,或者结果错误)?

7 个回答

3

在Python中,for语句是用来遍历一个叫做可迭代对象的。这种对象可以提供一个迭代器,让你能逐个获取里面的元素。for语句会依次从迭代器中取出下一个元素,把它赋值给目标变量,然后执行相关的代码块。

#   |name|   |iterable|
for party in feed.entry:
    # body...
    print(party.location.address.text)

在这个例子中,feed.entry就是可迭代对象,party是目标变量,而print ...是要执行的代码块。迭代器是由for语句自动请求的,它会保存当前的迭代状态,比如如果可迭代对象是一个列表,它会记录下一个元素的索引。


如果你之前用过C++,你可能知道经典的for (int i = 0; i < 10; ++i)循环代表的是外部迭代:迭代状态i是在可迭代对象外部保存的。这和Python的while循环是相似的:

# for (int i = 0; i < 10; ++i)
i = 0
while i < 10:
    i += 1
    # access the state of an iterable here

而新的for (auto party : entry)范围循环则代表了内部迭代:迭代状态是由一个单独的迭代器来保存的。这对应于Python的for循环。不过,Python的可迭代对象和迭代器的协议有一些显著的不同:Python的for使用iter(iterable)来获取一个迭代器,这个迭代器应该支持next(iterator),要么返回一个元素,要么抛出StopIteration

在Python中,定义for语句的方式如下:

# for party in feed.entry:
__iterator = iter(feed.entry)  # iterator -- not visible in containing scope
__iterating = True             # graceful exit to enter `else` clause
while __iterating:
    try:                          # attempt to...
        item = next(__iterator)   # ... get the next item
    except StopIteration:         # ... or stop
        __iterating = False       #     with a graceful exit
    else:
        party = item
        <suite>                # run the body with names bound
else:                          # entered in a graceful exit only
    <else suite>

(注意,从__iterating = True__iterating = False的整个代码块在外部是“不可见”的。实现中使用了各种优化,比如CPython允许内置迭代器返回C的NULL,而不是抛出Python的StopIteration。)

for语句只是定义了如何使用可迭代对象和迭代器。如果你主要熟悉外部迭代,了解可迭代对象和迭代器的工作原理会很有帮助。


iter(iterable)的调用有多种方式来获取迭代器,就像iter针对不同的结构类型进行了重载。

  • 如果type(iterable).__iter__被定义了,它会作为一个方法被调用,返回的结果就是迭代器。

  • 如果type(iterable).__getitem__被定义了,它会被一个通用的迭代器类型包装,这个迭代器会返回iterable[0]iterable[1],如果索引超出范围则抛出StopIteration

无论哪种方式,iter都会返回一个迭代器,或者抛出TypeError。迭代器是任何定义了__iter__(为了可重用性)和__next__(用于实际迭代)的类型。一般来说,迭代器是可以保存状态的对象,用来计算下一个__next__的元素。例如,列表的迭代器对应于这个对象:

class ListIterator:
    """Python equivalent of ``iter(:list)``"""
    # iterator holds iteration state - e.g. iterable and current index
    def __init__(self, iterable: list):
        self.iterable = iterable
        self.index = 0

    # __next__ fetches item and advances iteration state - e.g. index += 1
    def __next__(self):
        # attempt to produce an item
        try:
            item = self.iterable[self.index]
        except IndexError:  # translate indexing protocol to iteration protocol
            raise StopIteration
        # update iteration state
        self.index += 1
        return item

    # iterators can be iterated on in ``for`` statements etc.
    def __iter__(self):
        return self

(注意,通常会把这样的对象写成生成器函数。)

对列表进行索引或增加某个指针只是可迭代对象/迭代器协议的一个非常基础的例子。例如,一个迭代器可以是无状态的,并在__next__中使用random.random()来生成无限的随机数流。迭代器也可以保存外部信息的状态,比如逐步遍历文件系统。

6

feed.entry 是一个可以用来循环遍历的东西,它里面包含了一些特定类型的对象。这大概和 C++ 里的某些东西有点像:

for (feed::iterator party = feed.entry.begin(); party != feed.entry.end(); ++party) {
   cout << (*party).location.address.text;
}
25

feed.entry 是 feed 的一个属性,它的值必须是一个可以被遍历的对象(比如数组),否则这段代码就会出错。这个对象需要有一个叫做 iter 的方法,这个方法会返回一个迭代器对象。

迭代器有一个 next() 方法,这个方法会返回下一个元素,或者在没有更多元素时抛出一个异常。所以,Python 的 for 循环实际上是这样的:

iterator = feed.entry.__iter__()
while True:
    try:
        party = iterator.next()
    except StopIteration:
        # StopIteration exception is raised after last element
        break

    # loop code
    print party.location.address.text

撰写回答