当前位置: 首页 > 面试题库 >

Python for循环和迭代器行为

隆飞驰
2023-03-14
问题内容

我想了解更多有关的信息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

它们高效的原因与“参考”告诉您下一步是什么无关。它们之所以有效,是因为它们仅根据请求生成下一个项目。所有项目都不是一次生成的。实际上,您可以拥有一个无限生成器:

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函数将应用于对象以获取迭代器(顺便说一句:iter正如您所做的那样,不要在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

上面的代码与您的示例代码几乎没有区别。

实际上,for循环中最有趣的部分不是for,而是in。单独使用in会产生与产生不同的效果for
in,但是,了解in使用其参数的作用非常有用,因为它for in实现了非常相似的行为。

  • 单独使用时,in关键字首先调用对象的__contains__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__方法返回Python中称为iterator的值。基本上,迭代器是一个对象,您可以next()在1上使用内置的泛型函数。生成器只是迭代器的一种类型。

  • 如果__iter__成功调用,则in关键字将函数next()一次又一次地应用于可迭代对象。(请记住:可迭代对象可以是生成器,也可以是容器对象的迭代器,或任何其他可迭代对象。)实际上,更确切地说,它调用迭代器对象的__next__方法。

  • 如果对象没有__iter__返回迭代器的方法,in则使用对象的__getitem__方法2退回到旧式迭代协议。
  • 如果上述所有尝试均失败,您将获得一个TypeError异常。

如果您希望创建自己的对象类型以进行迭代(即,您可以使用,也可以for
inin在其上使用),那么了解生成器中yield使用的关键字(如上所述)很有用。

class MyIterable():
    def __iter__(self):
        yield 1

m = MyIterable()
for _ in m: print(_) # 1
1 in m # True

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

1请注意,要成为迭代器,对象必须实现迭代器协议。这仅意味着__next____iter__方法都必须 正确
实现(生成器带有“免费”此功能,因此您在使用它们时无需担心)。还要注意,该___next__方法实际上next在Python
2中(没有下划线)。

2请参 见此答案以了解创建可迭代类的不同方法。



 类似资料:
  • 本节我们探索的主题是迭代与循环。循环通常在计算机编程用用于自动执行重复性任务。 在Python中最常用的迭代形式就是for循环了。for循环允许你迭代出列表中所有的项,迭代出来后你可以做任何你想做的事情。 比如,我们创建了一个列表,并打印出其中所有元素的平方。 >>> for value in [0, 1, 2, 3, 4, 5]: ... print(value * value) ... 0 1

  • 如何在迭代中更改python迭代器? 例如: 此打印: 我想打印: 编辑:抱歉,这个问题让人们感到困惑。我对为什么当我在for循环中尝试更改迭代器时,for循环在下一次迭代中忽略它很感兴趣。 其他人已经提交了一个答案来清除我的困惑。范围创建一个列表,然后for循环将迭代器分配给列表中的下一个变量。

  • 迭代器无效是如何处理的,而不是循环? 例如,这段代码不起作用,因为迭代器在插入后无效: 但是,如果我用这个for循环替换while循环,它会正确编译和运行: 为什么for循环有效而while循环无效?

  • 我有一个用于队列的迭代器类(实现为循环数组)。我在下面附上代码。问题出在++运算符上。一旦它到达数组的末尾,它就会回到它的开始,因此迭代器会指向第一个元素。它工作得很好,但我没有办法使用这种方法实现then end()迭代器。在队列类中返回begin()和end()迭代器的函数可以在底部看到。end()迭代器应该指向队列的后部,但是当数组已满且后部等于数组的大小时,++运算符将循环返回,而不是让它

  • 2.3 循环与遍历 程序的比人肉强大的另一个特性就是可以任劳任怨地重复地做些单调无聊(或有聊)的 工作。本节介绍在 VimL 语言中,如何控制程序,命令其循环地按规则干活。 遍历集合变量 首先介绍的是如何依次访问列表如字典内的所有元素,毕竟在 2.1 节介绍的索引方法只 适于偶尔访问查看某个具体的元素。这里要用到的是for ... in 语法。 例如遍历列表: : let list = [0, 1

  • 问题内容: 我想知道为什么在 i 变量下面的代码中 仍然显示“ 5” 而不是显示 “ 1” 然后 “ 2” 然后 “ 3” ,依此类推?必须是一个范围问题,但由于在全局和dom范围中更改了i变量的范围,但仍然遇到相同的问题,因此我并没有真正了解它。当我提醒 我 的AJAX功能外,它工作得很好。 这是小提琴 编辑: 我选择了 @Tushar Gupta 解决方案,因为它最适合我的需要,但是又遇到了另