我想了解更多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]:
Run Code Online (Sandbox Code Playgroud)
在通过迭代器(In [2])的第一个循环后,它就好像被消耗并留空,所以第二个循环(In [3])不打印任何内容.
但是我从未为iter变量赋予新值.
在for循环的引擎下真正发生了什么?
Ric*_*ica 53
你的怀疑是正确的:迭代器已被消耗.
实际上,您的迭代器是一个生成器,它是一个只能迭代一次的对象.
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
Run Code Online (Sandbox Code Playgroud)
他们有效率的原因与告诉你"通过引用"下一步是什么无关.它们很有效,因为它们只根据要求生成下一个项目; 所有项目不会立即生成.事实上,你可以有一个无限的发电机:
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!
Run Code Online (Sandbox Code Playgroud)
其他一些更正有助于提高您的理解:
for in接受可迭代对象作为其第二个参数.list,或dict,或str对象(字符串),或提供所需功能的用户定义类型.iter函数应用于对象以获取迭代器(顺便说一下:不要iter像Python那样在Python中用作变量名 - 它是关键字之一).实际上,更确切地说,调用对象的__iter__方法(在大多数情况下,所有iter函数都是这样做的; __iter__是Python所谓的"魔术方法"之一).__iter__成功,则该函数next()在循环中一遍又一遍地应用于可迭代对象,并且将提供给的第一个变量for in分配给next()函数的结果.(请记住:可迭代对象可以是生成器,也可以是容器对象的迭代器,或任何其他可迭代对象.)实际上,更确切地说:它调用迭代器对象的__next__方法,这是另一种"魔术方法".for当循环结束next()引发StopIteration异常(当可迭代不具有另一个目的是产生时通常发生next()被调用).你可以for用这种方式"手动"实现python循环(可能不完美,但足够接近):
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
Run Code Online (Sandbox Code Playgroud)
上面和你的示例代码之间几乎没有区别.
实际上,for循环中更有趣的部分不是for,而是in.单独使用in会产生不同的效果for in,但理解in其参数的作用非常有用,因为它for in实现了非常相似的行为.
当单独使用时,in关键字首先调用对象的__contains__方法,这是另一种"魔术方法"(请注意,使用时会跳过此步骤for in).使用in一个容器本身,你可以做这样的事情:
1 in [1, 2, 3] # True
'He' in 'Hello' # True
3 in range(10) # True
'eH' in 'Hello'[::-1] # True
Run Code Online (Sandbox Code Playgroud)如果可迭代对象不是容器(即它没有__contains__方法),则in接下来尝试调用对象的__iter__方法.如前所述:该__iter__方法返回Python中已知的迭代器.基本上,迭代器是一个对象,您可以next()在1上使用内置泛型函数.生成器只是一种迭代器.
__iter__成功,则in关键字将函数next()一次又一次地应用于可迭代对象.(记住:可迭代对象可以是生成器,容器对象的迭代器,或任何其他可迭代对象.)实际上,更确切地说:它调用迭代器对象的__next__方法).__iter__返回迭代器的方法,in则使用对象的__getitem__方法2回退旧式迭代协议.TypeError异常.如果您希望创建自己的对象类型进行迭代(即,您可以使用它for in,或者只是in对它进行迭代),了解yield关键字(在生成器中使用)(如上所述)非常有用.
class MyIterable():
def __iter__(self):
yield 1
m = MyIterable()
for _ in m: print(_) # 1
1 in m # True
Run Code Online (Sandbox Code Playgroud)
将yield函数或方法转换为生成器而不是常规函数/方法的存在.__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
Run Code Online (Sandbox Code Playgroud)
1请注意,要成为迭代器,对象必须实现迭代器协议.这只意味着必须正确实现__next__和__iter__方法(生成器"免费"提供此功能,因此您在使用它时无需担心).另请注意,该方法实际上(没有下划线)在Python 2中.___next__next
2有关 创建可迭代类的不同方法,请参阅此答案.
Mar*_*cin 18
For循环基本上调用next应用于(__next__在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
Run Code Online (Sandbox Code Playgroud)
此时输入对象中没有下一个元素.这样做:
print(next(iter))
Run Code Online (Sandbox Code Playgroud)
会导致StopIteration异常抛出.此时for将停止.并且迭代器可以是任何对象,它将响应next()函数并在没有更多元素时抛出异常.它不必是任何指针或引用(在C/C++意义上的python中没有这样的东西),链表等.
小智 6
python中有一个迭代器协议,它定义了for语句对列表和dicts的行为,以及其他可以循环的东西.
迭代器协议的工作方式通常是python生成器的形式.我们yield只要我们有一个值,直到我们到达终点,然后我们提出一个值StopIteration
所以让我们编写自己的迭代器:
def my_iter():
yield 1
yield 2
yield 3
raise StopIteration()
for i in my_iter():
print i
Run Code Online (Sandbox Code Playgroud)
结果是:
1
2
3
Run Code Online (Sandbox Code Playgroud)
关于这一点需要注意几点.my_iter是一个函数.my_iter()返回一个迭代器.
如果我用这样的迭代器编写代码:
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
Run Code Online (Sandbox Code Playgroud)
结果与上述相同.当我们进入第二个for循环时,iter就会耗尽.
但对于更复杂的事情,这是相当简单的呢?也许在一个循环中为什么不呢?
def capital_iter(name):
for x in name:
yield x.upper()
raise StopIteration()
for y in capital_iter('bobert'):
print y
Run Code Online (Sandbox Code Playgroud)
当它运行时,我们在字符串类型(内置于iter中)使用迭代器.反过来,这允许我们对它运行for循环,并产生结果,直到我们完成.
B
O
B
E
R
T
Run Code Online (Sandbox Code Playgroud)
所以现在这引出了一个问题,那么迭代器中的收益之间会发生什么呢?
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
Run Code Online (Sandbox Code Playgroud)
答案是函数在等待下一次调用next()的yield时暂停.
B
O
B
Hey There!
E
R
T
Traceback (most recent call last):
File "", line 13, in
StopIteration
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
27334 次 |
| 最近记录: |