Python for 循环和迭代器行为
Python for loop and iterator behavior
我想了解更多关于iterators
的知识,所以如果我错了,请纠正我。
迭代器是一个对象,它具有指向下一个对象的指针,并作为缓冲区或流(即链表)读取。它们特别高效,因为它们所做的只是通过引用而不是使用索引告诉您下一步是什么。
但是我仍然不明白为什么会发生以下行为:
In [1]: iter = (i for i in range(5))
In [2]: for _ in iter:
....: print _
....:
0
1
2
3
4
In [3]: for _ in iter:
....: print _
....:
In [4]:
在迭代器 (In [2]
) 的第一个循环之后,就好像它被消耗掉了并且留空了,所以第二个循环 (In [3]
) 什么都不打印。
但是我从未为 iter
变量分配新值。
for
循环背后到底发生了什么?
您的猜测是正确的:迭代器已被消耗。
实际上,您的迭代器是一个 generator,它是一个可以迭代 一次的对象。
type((i for i in range(5))) # says it's type generator
def another_generator():
yield 1 # the yield expression makes it a generator, not a function
type(another_generator()) # also a generator
它们高效的原因与告诉你下一步是什么无关"by reference."它们之所以高效,是因为它们只根据请求生成下一个项目;所有项目都不是一次生成的。事实上,你可以拥有一个无限的发电机:
def my_gen():
while True:
yield 1 # again: yield means it is a generator, not a function
for _ in my_gen(): print(_) # hit ctl+c to stop this infinite loop!
一些其他更正以帮助提高您的理解:
- 生成器不是指针,其行为也不像您在其他语言中所熟悉的那样。
- 与其他语言的区别之一:如上所述,生成器的每个结果都是动态生成的。在请求之前不会产生下一个结果。
- 关键字组合
for
in
接受一个可迭代对象作为它的第二个参数。
- 可迭代对象可以是生成器,如您的示例中所示,但它也可以是任何其他可迭代对象,例如
list
、dict
或 str
对象(字符串),或提供所需功能的用户定义类型。
-
iter
function is applied to the object to get an iterator (by the way: don't use iter
as a variable name in Python, as you have done - it is one of the keywords). Actually, to be more precise, the object's __iter__
method 被调用(在大多数情况下,所有 iter
函数都会执行;__iter__
是 Python 的其中一个 -称为 "magic methods").
- 如果调用
__iter__
成功,函数next()
is applied to the iterable object over and over again, in a loop, and the first variable supplied to for
in
is assigned to the result of the next()
function. (Remember: the iterable object could be a generator, or a container object's iterator, or any other iterable object.) Actually, to be more precise: it calls the iterator object's __next__
方法,也就是另一个"magic method"。
- 当
next()
引发 StopIteration
异常时,for
循环结束(这通常发生在调用 next()
时可迭代对象没有另一个对象要产生时) .
您可以 "manually" 在 python 中实现一个 for
循环(可能不完美,但足够接近):
try:
temp = iterable.__iter__()
except AttributeError():
raise TypeError("'{}' object is not iterable".format(type(iterable).__name__))
else:
while True:
try:
_ = temp.__next__()
except StopIteration:
break
except AttributeError:
raise TypeError("iter() returned non-iterator of type '{}'".format(type(temp).__name__))
# this is the "body" of the for loop
continue
上面的示例代码与您的示例代码几乎没有区别。
实际上,for
循环中更有趣的部分不是 for
,而是 in
。单独使用 in
产生的效果与 for
in
不同,但是理解 in
对其参数的作用非常有用,因为 for
in
实现了非常相似的行为。
单独使用时,in
关键字首先调用对象的__contains__
method,这是另一个"magic method"(注意使用时跳过此步骤for
in
)。在容器上单独使用 in
,你可以这样做:
1 in [1, 2, 3] # True
'He' in 'Hello' # True
3 in range(10) # True
'eH' in 'Hello'[::-1] # True
如果可迭代对象不是容器(即它没有 __contains__
方法),in
接下来会尝试调用对象的 __iter__
方法。如前所述:__iter__
方法 return 在 Python 中被称为 1 上的 iterator. Basically, an iterator is an object that you can use the built-in generic function next()
。生成器只是迭代器的一种。
- 如果调用
__iter__
成功,in
关键字应用函数next()
to the iterable object over and over again. (Remember: the iterable object could be a generator, or a container object's iterator, or any other iterable object.) Actually, to be more precise: it calls the iterator object's __next__
方法)。
- 如果对象没有return迭代器的
__iter__
方法,in
然后使用对象的__getitem__
方法2。
- 如果以上所有尝试都失败,您将得到
TypeError
exception。
如果您希望创建自己的对象类型进行迭代(即,您可以使用 for
in
,或仅使用 in
),了解它很有用关于 yield
关键字,它在 generators 中使用(如上所述)。
class MyIterable():
def __iter__(self):
yield 1
m = MyIterable()
for _ in m: print(_) # 1
1 in m # True
yield
的存在将函数或方法变成了生成器,而不是常规的 function/method。如果您使用生成器,则不需要 __next__
方法(它会自动带来 __next__
)。
如果您希望创建自己的容器对象类型(即,您可以单独对其使用 in
,但不能使用 for
in
),您只需要 __contains__
方法。
class MyUselessContainer():
def __contains__(self, obj):
return True
m = MyUselessContainer()
1 in m # True
'Foo' in m # True
TypeError in m # True
None in m # True
1 请注意,要成为迭代器,对象必须实现 the iterator protocol. This only means that both the __next__
and __iter__
methods must be correctly implemented (generators come with this functionality "for free", so you don't need to worry about it when using them). Also note that the ___next__
method is actually next
(no underscores) in Python 2。
2 请参阅 this answer 了解创建可迭代对象 类.
的不同方法
For 循环基本上调用应用于对象的 next
方法 (__next__
in Python 3).
你可以简单地模拟这个:
iter = (i for i in range(5))
print(next(iter))
print(next(iter))
print(next(iter))
print(next(iter))
print(next(iter))
# this prints 1 2 3 4
此时输入对象中没有下一个元素。所以这样做:
print(next(iter))
将导致抛出 StopIteration
异常。此时for
就会停止。迭代器可以是 any object ,它将响应 next()
函数并在没有更多元素时抛出异常。它不必是任何指针或引用(在 C/C++ 意义上无论如何 python 中没有这样的东西)、链表等
python 中有一个迭代器协议,它定义了 for
语句如何处理列表和字典,以及其他可以循环的东西。
迭代器协议通常以 python 生成器的形式工作。我们yield
一个值只要我们有一个值直到我们到达终点然后我们提高StopIteration
所以让我们编写自己的迭代器:
def my_iter():
yield 1
yield 2
yield 3
raise StopIteration()
for i in my_iter():
print i
结果是:
1
2
3
有几点需要注意。 my_iter 是一个函数。 my_iter() returns 一个迭代器。
如果我改用这样的迭代器编写:
j = my_iter() #j is the iterator that my_iter() returns
for i in j:
print i #this loop runs until the iterator is exhausted
for i in j:
print i #the iterator is exhausted so we never reach this line
结果同上。当我们进入第二个 for 循环时,iter 已经耗尽。
但这太简单了,那么更复杂的事情呢?为什么不呢?
def capital_iter(name):
for x in name:
yield x.upper()
raise StopIteration()
for y in capital_iter('bobert'):
print y
而当它 运行s 时,我们在字符串类型上使用迭代器(内置于 iter)。反过来,这允许我们 运行 在其上进行 for 循环,并在完成之前产生结果。
B
O
B
E
R
T
所以现在这回避了问题,那么迭代器中的 yield 之间会发生什么?
j = capital_iter("bobert")
print i.next()
print i.next()
print i.next()
print("Hey there!")
print i.next()
print i.next()
print i.next()
print i.next() #Raises StopIteration
答案是函数在 yield 处暂停,等待下一次调用 next()。
B
O
B
Hey There!
E
R
T
Traceback (most recent call last):
File "", line 13, in
StopIteration
概念 1
All generators are iterators but all iterators are not generator
概念 2
An iterator is an object with a next (Python 2) or next (Python 3)
method.
概念 3
Quoting from wiki
Generators Generators
functions allow you to declare a function that behaves like an
iterator, i.e. it can be used in a for loop.
你的情况
>>> it = (i for i in range(5))
>>> type(it)
<type 'generator'>
>>> callable(getattr(it, 'iter', None))
False
>>> callable(getattr(it, 'next', None))
True
关于 iter()
和 __getitem__
类 缺少自己的 __iter__
方法的行为的一些额外细节。
在 __iter__
之前有 __getitem__
。如果 __getitem__
与 0
- len(obj)-1
中的 int
一起工作,那么 iter()
支持这些对象。它将构造一个新的迭代器,重复调用 __getitem__
和 0
、1
、2
、...
,直到它得到一个 IndexError
,它转换为 StopIteration
.
有关创建迭代器的不同方法的更多详细信息,请参阅 this answer。
5.迭代器和生成器
5.1。迭代器
我们使用 for 语句循环列表。
>>> for i in [1, 2, 3, 4]:
... print i,
...
1
2
3
4
如果我们将它与字符串一起使用,它会遍历其字符。
>>> for c in "python":
... print c
...
p
y
t
h
o
n
如果我们将它与字典一起使用,它会遍历它的键。
>>> for k in {"x": 1, "y": 2}:
... print k
...
y
x
如果我们将它与文件一起使用,它会遍历文件的行。
>>> for line in open("a.txt"):
... print line,
...
first line
second line
因此有许多类型的对象可以与 for 循环一起使用。这些被称为可迭代对象。
有许多函数使用这些可迭代对象。
>>> ",".join(["a", "b", "c"])
'a,b,c'
>>> ",".join({"x": 1, "y": 2})
'y,x'
>>> list("python")
['p', 'y', 't', 'h', 'o', 'n']
>>> list({"x": 1, "y": 2})
['y', 'x']
5.1.1。迭代协议
内置函数 iter 采用可迭代对象和 return 迭代器。
>>> x = iter([1, 2, 3])
>>> x
<listiterator object at 0x1004ca850>
>>> x.next()
1
>>> x.next()
2
>>> x.next()
3
>>> x.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
每次我们在迭代器上调用 next 方法时都会为我们提供下一个元素。如果没有更多元素,它会引发 StopIteration。
迭代器实现为 类。这是一个像内置 xrange 函数一样工作的迭代器。
class yrange:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
iter 方法使对象可迭代。在幕后,iter 函数调用给定对象的 iter 方法。
iter的return值是迭代器。它应该有一个 next 方法并在没有更多元素时引发 StopIteration。
让我们试试看:
>>> y = yrange(3)
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 14, in next
停止迭代
许多内置函数接受迭代器作为参数。
>>> list(yrange(5))
[0, 1, 2, 3, 4]
>>> sum(yrange(5))
10
在上面的例子中,iterable 和 iterator 都是同一个对象。请注意 iter 方法 returned self.不必总是这样。
class zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
return zrange_iter(self.n)
class zrange_iter:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
# Iterators are iterables too.
# Adding this functions to make them so.
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
如果可迭代对象和迭代器是同一个对象,则在单次迭代中使用。
>>> y = yrange(5)
>>> list(y)
[0, 1, 2, 3, 4]
>>> list(y)
[]
>>> z = zrange(5)
>>> list(z)
[0, 1, 2, 3, 4]
>>> list(z)
[0, 1, 2, 3, 4]
5.2。发电机
生成器简化了迭代器的创建。生成器是一种生成一系列结果而不是单个值的函数。
def yrange(n):
i = 0
while i < n:
yield i
i += 1
每次执行 yield 语句时,函数都会生成一个新值。
>>> y = yrange(3)
>>> y
<generator object yrange at 0x401f30>
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
所以生成器也是迭代器。您不必担心迭代器协议。
“生成器”一词被混淆地用于表示生成的函数和生成的内容。在本章中,我将使用“生成器”一词来表示生成的对象,使用“生成器函数”来表示生成它的函数。
你能想一想它在内部是如何工作的吗?
当生成器函数被调用时,它 return 是一个生成器对象,甚至没有开始执行该函数。当第一次调用 next 方法时,函数开始执行,直到到达 yield 语句。产生的值由下一次调用return编辑。
以下示例演示了 yield 和调用生成器对象的 next 方法之间的相互作用。
>>> def foo():
... print "begin"
... for i in range(3):
... print "before yield", i
... yield i
... print "after yield", i
... print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0
0
>>> f.next()
after yield 0
before yield 1
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
让我们看一个例子:
def integers():
"""Infinite sequence of integers."""
i = 1
while True:
yield i
i = i + 1
def squares():
for i in integers():
yield i * i
def take(n, seq):
"""Returns first n values from the given sequence."""
seq = iter(seq)
result = []
try:
for i in range(n):
result.append(seq.next())
except StopIteration:
pass
return result
print take(5, squares()) # prints [1, 4, 9, 16, 25]
我想了解更多关于iterators
的知识,所以如果我错了,请纠正我。
迭代器是一个对象,它具有指向下一个对象的指针,并作为缓冲区或流(即链表)读取。它们特别高效,因为它们所做的只是通过引用而不是使用索引告诉您下一步是什么。
但是我仍然不明白为什么会发生以下行为:
In [1]: iter = (i for i in range(5))
In [2]: for _ in iter:
....: print _
....:
0
1
2
3
4
In [3]: for _ in iter:
....: print _
....:
In [4]:
在迭代器 (In [2]
) 的第一个循环之后,就好像它被消耗掉了并且留空了,所以第二个循环 (In [3]
) 什么都不打印。
但是我从未为 iter
变量分配新值。
for
循环背后到底发生了什么?
您的猜测是正确的:迭代器已被消耗。
实际上,您的迭代器是一个 generator,它是一个可以迭代 一次的对象。
type((i for i in range(5))) # says it's type generator
def another_generator():
yield 1 # the yield expression makes it a generator, not a function
type(another_generator()) # also a generator
它们高效的原因与告诉你下一步是什么无关"by reference."它们之所以高效,是因为它们只根据请求生成下一个项目;所有项目都不是一次生成的。事实上,你可以拥有一个无限的发电机:
def my_gen():
while True:
yield 1 # again: yield means it is a generator, not a function
for _ in my_gen(): print(_) # hit ctl+c to stop this infinite loop!
一些其他更正以帮助提高您的理解:
- 生成器不是指针,其行为也不像您在其他语言中所熟悉的那样。
- 与其他语言的区别之一:如上所述,生成器的每个结果都是动态生成的。在请求之前不会产生下一个结果。
- 关键字组合
for
in
接受一个可迭代对象作为它的第二个参数。 - 可迭代对象可以是生成器,如您的示例中所示,但它也可以是任何其他可迭代对象,例如
list
、dict
或str
对象(字符串),或提供所需功能的用户定义类型。 -
iter
function is applied to the object to get an iterator (by the way: don't useiter
as a variable name in Python, as you have done - it is one of the keywords). Actually, to be more precise, the object's__iter__
method 被调用(在大多数情况下,所有iter
函数都会执行;__iter__
是 Python 的其中一个 -称为 "magic methods"). - 如果调用
__iter__
成功,函数next()
is applied to the iterable object over and over again, in a loop, and the first variable supplied tofor
in
is assigned to the result of thenext()
function. (Remember: the iterable object could be a generator, or a container object's iterator, or any other iterable object.) Actually, to be more precise: it calls the iterator object's__next__
方法,也就是另一个"magic method"。 - 当
next()
引发StopIteration
异常时,for
循环结束(这通常发生在调用next()
时可迭代对象没有另一个对象要产生时) .
您可以 "manually" 在 python 中实现一个 for
循环(可能不完美,但足够接近):
try:
temp = iterable.__iter__()
except AttributeError():
raise TypeError("'{}' object is not iterable".format(type(iterable).__name__))
else:
while True:
try:
_ = temp.__next__()
except StopIteration:
break
except AttributeError:
raise TypeError("iter() returned non-iterator of type '{}'".format(type(temp).__name__))
# this is the "body" of the for loop
continue
上面的示例代码与您的示例代码几乎没有区别。
实际上,for
循环中更有趣的部分不是 for
,而是 in
。单独使用 in
产生的效果与 for
in
不同,但是理解 in
对其参数的作用非常有用,因为 for
in
实现了非常相似的行为。
单独使用时,
in
关键字首先调用对象的__contains__
method,这是另一个"magic method"(注意使用时跳过此步骤for
in
)。在容器上单独使用in
,你可以这样做:1 in [1, 2, 3] # True 'He' in 'Hello' # True 3 in range(10) # True 'eH' in 'Hello'[::-1] # True
如果可迭代对象不是容器(即它没有
__contains__
方法),in
接下来会尝试调用对象的__iter__
方法。如前所述:__iter__
方法 return 在 Python 中被称为 1 上的 iterator. Basically, an iterator is an object that you can use the built-in generic functionnext()
。生成器只是迭代器的一种。- 如果调用
__iter__
成功,in
关键字应用函数next()
to the iterable object over and over again. (Remember: the iterable object could be a generator, or a container object's iterator, or any other iterable object.) Actually, to be more precise: it calls the iterator object's__next__
方法)。 - 如果对象没有return迭代器的
__iter__
方法,in
然后使用对象的__getitem__
方法2。 - 如果以上所有尝试都失败,您将得到
TypeError
exception。
如果您希望创建自己的对象类型进行迭代(即,您可以使用 for
in
,或仅使用 in
),了解它很有用关于 yield
关键字,它在 generators 中使用(如上所述)。
class MyIterable():
def __iter__(self):
yield 1
m = MyIterable()
for _ in m: print(_) # 1
1 in m # True
yield
的存在将函数或方法变成了生成器,而不是常规的 function/method。如果您使用生成器,则不需要 __next__
方法(它会自动带来 __next__
)。
如果您希望创建自己的容器对象类型(即,您可以单独对其使用 in
,但不能使用 for
in
),您只需要 __contains__
方法。
class MyUselessContainer():
def __contains__(self, obj):
return True
m = MyUselessContainer()
1 in m # True
'Foo' in m # True
TypeError in m # True
None in m # True
1 请注意,要成为迭代器,对象必须实现 the iterator protocol. This only means that both the __next__
and __iter__
methods must be correctly implemented (generators come with this functionality "for free", so you don't need to worry about it when using them). Also note that the ___next__
method is actually next
(no underscores) in Python 2。
2 请参阅 this answer 了解创建可迭代对象 类.
的不同方法For 循环基本上调用应用于对象的 next
方法 (__next__
in Python 3).
你可以简单地模拟这个:
iter = (i for i in range(5))
print(next(iter))
print(next(iter))
print(next(iter))
print(next(iter))
print(next(iter))
# this prints 1 2 3 4
此时输入对象中没有下一个元素。所以这样做:
print(next(iter))
将导致抛出 StopIteration
异常。此时for
就会停止。迭代器可以是 any object ,它将响应 next()
函数并在没有更多元素时抛出异常。它不必是任何指针或引用(在 C/C++ 意义上无论如何 python 中没有这样的东西)、链表等
python 中有一个迭代器协议,它定义了 for
语句如何处理列表和字典,以及其他可以循环的东西。
迭代器协议通常以 python 生成器的形式工作。我们yield
一个值只要我们有一个值直到我们到达终点然后我们提高StopIteration
所以让我们编写自己的迭代器:
def my_iter():
yield 1
yield 2
yield 3
raise StopIteration()
for i in my_iter():
print i
结果是:
1
2
3
有几点需要注意。 my_iter 是一个函数。 my_iter() returns 一个迭代器。
如果我改用这样的迭代器编写:
j = my_iter() #j is the iterator that my_iter() returns
for i in j:
print i #this loop runs until the iterator is exhausted
for i in j:
print i #the iterator is exhausted so we never reach this line
结果同上。当我们进入第二个 for 循环时,iter 已经耗尽。
但这太简单了,那么更复杂的事情呢?为什么不呢?
def capital_iter(name):
for x in name:
yield x.upper()
raise StopIteration()
for y in capital_iter('bobert'):
print y
而当它 运行s 时,我们在字符串类型上使用迭代器(内置于 iter)。反过来,这允许我们 运行 在其上进行 for 循环,并在完成之前产生结果。
B
O
B
E
R
T
所以现在这回避了问题,那么迭代器中的 yield 之间会发生什么?
j = capital_iter("bobert")
print i.next()
print i.next()
print i.next()
print("Hey there!")
print i.next()
print i.next()
print i.next()
print i.next() #Raises StopIteration
答案是函数在 yield 处暂停,等待下一次调用 next()。
B
O
B
Hey There!
E
R
T
Traceback (most recent call last):
File "", line 13, in
StopIteration
概念 1
All generators are iterators but all iterators are not generator
概念 2
An iterator is an object with a next (Python 2) or next (Python 3) method.
概念 3
Quoting from wiki Generators Generators functions allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop.
你的情况
>>> it = (i for i in range(5))
>>> type(it)
<type 'generator'>
>>> callable(getattr(it, 'iter', None))
False
>>> callable(getattr(it, 'next', None))
True
关于 iter()
和 __getitem__
类 缺少自己的 __iter__
方法的行为的一些额外细节。
在 __iter__
之前有 __getitem__
。如果 __getitem__
与 0
- len(obj)-1
中的 int
一起工作,那么 iter()
支持这些对象。它将构造一个新的迭代器,重复调用 __getitem__
和 0
、1
、2
、...
,直到它得到一个 IndexError
,它转换为 StopIteration
.
有关创建迭代器的不同方法的更多详细信息,请参阅 this answer。
5.迭代器和生成器
5.1。迭代器
我们使用 for 语句循环列表。
>>> for i in [1, 2, 3, 4]:
... print i,
...
1
2
3
4
如果我们将它与字符串一起使用,它会遍历其字符。
>>> for c in "python":
... print c
...
p
y
t
h
o
n
如果我们将它与字典一起使用,它会遍历它的键。
>>> for k in {"x": 1, "y": 2}:
... print k
...
y
x
如果我们将它与文件一起使用,它会遍历文件的行。
>>> for line in open("a.txt"):
... print line,
...
first line
second line
因此有许多类型的对象可以与 for 循环一起使用。这些被称为可迭代对象。
有许多函数使用这些可迭代对象。
>>> ",".join(["a", "b", "c"])
'a,b,c'
>>> ",".join({"x": 1, "y": 2})
'y,x'
>>> list("python")
['p', 'y', 't', 'h', 'o', 'n']
>>> list({"x": 1, "y": 2})
['y', 'x']
5.1.1。迭代协议
内置函数 iter 采用可迭代对象和 return 迭代器。
>>> x = iter([1, 2, 3])
>>> x
<listiterator object at 0x1004ca850>
>>> x.next()
1
>>> x.next()
2
>>> x.next()
3
>>> x.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
每次我们在迭代器上调用 next 方法时都会为我们提供下一个元素。如果没有更多元素,它会引发 StopIteration。
迭代器实现为 类。这是一个像内置 xrange 函数一样工作的迭代器。
class yrange:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
iter 方法使对象可迭代。在幕后,iter 函数调用给定对象的 iter 方法。
iter的return值是迭代器。它应该有一个 next 方法并在没有更多元素时引发 StopIteration。
让我们试试看:
>>> y = yrange(3)
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 14, in next
停止迭代
许多内置函数接受迭代器作为参数。
>>> list(yrange(5))
[0, 1, 2, 3, 4]
>>> sum(yrange(5))
10
在上面的例子中,iterable 和 iterator 都是同一个对象。请注意 iter 方法 returned self.不必总是这样。
class zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
return zrange_iter(self.n)
class zrange_iter:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
# Iterators are iterables too.
# Adding this functions to make them so.
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
如果可迭代对象和迭代器是同一个对象,则在单次迭代中使用。
>>> y = yrange(5)
>>> list(y)
[0, 1, 2, 3, 4]
>>> list(y)
[]
>>> z = zrange(5)
>>> list(z)
[0, 1, 2, 3, 4]
>>> list(z)
[0, 1, 2, 3, 4]
5.2。发电机
生成器简化了迭代器的创建。生成器是一种生成一系列结果而不是单个值的函数。
def yrange(n):
i = 0
while i < n:
yield i
i += 1
每次执行 yield 语句时,函数都会生成一个新值。
>>> y = yrange(3)
>>> y
<generator object yrange at 0x401f30>
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
所以生成器也是迭代器。您不必担心迭代器协议。
“生成器”一词被混淆地用于表示生成的函数和生成的内容。在本章中,我将使用“生成器”一词来表示生成的对象,使用“生成器函数”来表示生成它的函数。
你能想一想它在内部是如何工作的吗?
当生成器函数被调用时,它 return 是一个生成器对象,甚至没有开始执行该函数。当第一次调用 next 方法时,函数开始执行,直到到达 yield 语句。产生的值由下一次调用return编辑。
以下示例演示了 yield 和调用生成器对象的 next 方法之间的相互作用。
>>> def foo():
... print "begin"
... for i in range(3):
... print "before yield", i
... yield i
... print "after yield", i
... print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0
0
>>> f.next()
after yield 0
before yield 1
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
让我们看一个例子:
def integers():
"""Infinite sequence of integers."""
i = 1
while True:
yield i
i = i + 1
def squares():
for i in integers():
yield i * i
def take(n, seq):
"""Returns first n values from the given sequence."""
seq = iter(seq)
result = []
try:
for i in range(n):
result.append(seq.next())
except StopIteration:
pass
return result
print take(5, squares()) # prints [1, 4, 9, 16, 25]