Python中的可迭代对象的for循环是如何工作的?
我对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 个回答
在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()
来生成无限的随机数流。迭代器也可以保存外部信息的状态,比如逐步遍历文件系统。
feed.entry 是一个可以用来循环遍历的东西,它里面包含了一些特定类型的对象。这大概和 C++ 里的某些东西有点像:
for (feed::iterator party = feed.entry.begin(); party != feed.entry.end(); ++party) {
cout << (*party).location.address.text;
}
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