【问题标题】:When should I use iter function?什么时候应该使用 iter 函数?
【发布时间】:2020-05-29 16:35:08
【问题描述】:

这段代码

for i in range(100, -1, -1):
   print(i)

同:

for i in iter(range(100, -1, -1)):
   print(i)

从 0 .. 100 个数字降序排列的列表中的哪个打印数字。

我知道哨兵属性一旦到达它就会停止,但除此之外我应该什么时候考虑使用iter() 函数?

谢谢。

【问题讨论】:

  • 有时,您希望在同一个可迭代对象上有多个独立的迭代器。在itertools documentationgrouper 函数中可以找到一个很好的例子。
  • @G.Anderson 不,我的问题不是关于 range(),而是关于 iter() 函数,我也可以对列表执行此操作。即["aa", "bb", "ccc"] ..

标签: python list loops


【解决方案1】:

任何时候你想要一个迭代器。这就是iter 的用途,它为您传递给它的内容返回一个迭代器。因此,请考虑以下生成器,它利用了迭代器的属性,即它是单次传递且可耗尽的,从迭代器中提取迭代器应该返回迭代器本身,而不是给你一个新的。

In [19]: import itertools

In [20]: def chunk_by_n(iterable, n):
    ...:     islice = itertools.islice
    ...:     iterator = iter(iterable)
    ...:     chunk = list(islice(iterator, n))
    ...:     while chunk:
    ...:         yield chunk
    ...:         chunk = list(islice(iterator, n))
    ...:

In [21]: iterable = range(100)

In [22]: chunks = chunk_by_n(iterable, 3)

In [23]: next(chunks)
Out[23]: [0, 1, 2]

In [24]: next(chunks)
Out[24]: [3, 4, 5]

In [25]: next(chunks)
Out[25]: [6, 7, 8]

现在,看看如果我们不从输入中创建一个迭代器会发生什么:

In [26]: def chunk_by_n(iterable, n):
    ...:     islice = itertools.islice
    ...:     #iterator = iter(iterable)
    ...:     iterator = iterable
    ...:     chunk = list(islice(iterator, n))
    ...:     while chunk:
    ...:         yield chunk
    ...:         chunk = list(islice(iterator, n))
    ...:

In [27]: chunks = chunk_by_n(iterable, 3)

In [28]: next(chunks)
Out[28]: [0, 1, 2]

In [29]: next(chunks)
Out[29]: [0, 1, 2]

In [30]: next(chunks)
Out[30]: [0, 1, 2]

【讨论】:

    【解决方案2】:

    这是一个例子。假设我有一个项目列表,其中项目计数后跟该项目数量,一遍又一遍。

    [1, 'some data', 3, 'some data', 'some data', 'some data', 2, ...]
    

    我可能想使用嵌套的for 循环遍历这些数据:外部循环获取项目计数,内部循环获取列表的以下n 元素。两个循环可以共享一个显式迭代器:

    from itertools import islice
    
    my_data = [1, "a", 3, "b", "c", "d", 2, "e", "f"]
    my_iter = iter(my_data)
    for i, count in enumerate(my_iter):
        print(f"Round {i}")
        for item in islice(my_iter, count):
            print(f'  {item}')
    

    此代码产生输出

    Round 0
      a
    Round 1
      b
      c
      d
    Round 2
      e
      f
    

    【讨论】:

      【解决方案3】:

      不是为了这个。 iter() 非常适合将需要内存的迭代器转换为生成器,或者将已经存在的非生成器迭代器转换为可以调用next() 的对象。

      但是range() 已经是一个生成器(嗯,不是真的,但它的行为与生成器非常相似并且具有相同的优点),所以在这种情况下没有任何好处。

      就此而言,对于一般的for 循环,如果您的可迭代对象已经完全存在,那么从中制作iter() 是没有意义的——for 关键字已经在幕后完成了。

      【讨论】:

      • 重要提示:尽管被延迟评估和迭代,Range is NOT a generator
      • 什么是“非生成器可迭代”? range 之类的东西是可迭代的,但不是迭代器。 iter(range(...)) 返回 range_iterator 类型的东西,它(如你所料)一个迭代器。
      • @chepner 当我在考虑非生成器可迭代时,我在考虑可迭代的数据结构,主要是 - lists、sets、dicts 等,其中内存是已经分配了。
      • iter 不会将可迭代对象转换为生成器。生成器只是一种可迭代对象,一种产生的函数。 iter 通过调用 __iter__ 方法将可迭代对象转换为迭代器。在 OP 的情况下不需要 iter 的唯一原因是因为 for 总是从给定的迭代本身创建一个迭代器,就像 iter 所做的那样,使 iter 多余。
      • "iter() 非常适合将需要内存的可迭代对象转换为生成器" 这是错误的,iter 从可迭代对象返回迭代器。另外:“但是 range() 已经是一个生成器(好吧,不是真的,但它的行为与一个非常相似并且具有相同的优势”不,它的行为不像一个生成器。它没有__next__方法,特别是。它是一个 iterable 容器,而不是 iteratoriter 用于当您需要迭代器时,它可能用于手动迭代出于某些原因,或出于其他各种原因。您可能想要range
      猜你喜欢
      • 2020-07-16
      • 2023-04-02
      • 2011-04-15
      • 2017-04-10
      • 2012-03-19
      • 2018-05-12
      • 2018-12-11
      • 1970-01-01
      • 2022-09-28
      相关资源
      最近更新 更多