【问题标题】:Cleanest way to get last item from Python iterator从 Python 迭代器获取最后一项的最简洁方法
【发布时间】:2011-01-09 11:31:00
【问题描述】:

在 Python 2.6 中从迭代器中获取最后一项的最佳方法是什么?例如,说

my_iter = iter(range(5))

my_iter 获取4 的最短代码/最简洁的方法是什么?

我可以这样做,但似乎效率不高:

[x for x in my_iter][-1]

【问题讨论】:

  • 迭代器假定您想要遍历元素而不是真正访问最后一个元素。是什么阻止您简单地使用 range(5)[-1] ?
  • @Frank - 我认为实际的迭代器比 iter(range(5)) 更复杂和/或更远和/或更难控制
  • @Frank:事实上,它实际上是一个提供迭代器的更复杂的生成器函数。我只是制作了这个示例,以便简单明了地发生了什么。
  • 如果你想要迭代器的最后一项,你很可能做错了什么。但答案是,实际上没有任何更清洁的方法可以遍历迭代器。这是因为迭代器没有大小,事实上,可能永远不会结束,因此可能没有最后一项。 (当然,这意味着您的代码将永远运行)。所以挥之不去的问题是:为什么要迭代器的最后一项?
  • @Peter:请更新您的问题。不要将一堆 cmets 添加到您拥有的问题中。请更新问题并删除 cmets。

标签: python python-3.x python-2.7 iterator


【解决方案1】:
item = defaultvalue
for item in my_iter:
    pass

【讨论】:

  • 为什么要使用占位符“defaultvalue”?为什么不None?这正是None 的用途。您是否建议某些特定于功能的默认值甚至可能是正确的?如果迭代器实际上没有迭代,那么带外值比某些误导性的函数特定默认值有意义。
  • 默认值只是我的示例的占位符。如果您想使用None 作为默认值,那是您的选择。 None 并不总是最明智的默认设置,甚至可能不会超出范围。我个人倾向于使用“defaultvalue = object()”来确保它是一个真正独特的值。我只是表明默认的选择超出了这个例子的范围。
  • @S.Lott:也许区分空迭代器和最终值为 None 的迭代器之间的区别很有用
  • 所有内置容器类型的所有迭代器都存在设计错误?第一次听说 :)
  • 虽然这可能是更快的解决方案,但它依赖于 for 循环中的变量泄漏(对于某些人来说是一个功能,对于另一些人来说是一个错误 - 可能 FP-guys 感到震惊)。无论如何,Guido 说这将始终以这种方式工作,因此使用起来很安全。
【解决方案2】:

如果您使用的是 Python 3.x:

*_, last = iterator # for a better understanding check PEP 448
print(last)

如果您使用的是 python 2.7:

last = next(iterator)
for last in iterator:
    continue
print last


旁注:

通常,上面介绍的解决方案是您在常规情况下所需要的,但如果您要处理大量数据,使用大小为 1 的deque 会更有效。(source)

from collections import deque

#aa is an interator
aa = iter('apple')

dd = deque(aa, maxlen=1)
last_element = dd.pop()

【讨论】:

  • @virtualxtc nope _ 是 python 中的特殊变量,用于存储最后一个值或表示我不关心该值,因此可以清理。
  • Python 3 解决方案的内存效率不高。
  • @DhiaTN 是的,你完全正确。实际上,我非常喜欢您展示的 Python 3 习语。我只是想澄清一下,它不适用于“大数据”。为此,我使用了 collections.deque,这恰好是快速且内存高效的(请参阅 martin23487234 的解决方案)。
  • 如果iterator 为空,*_, last = iterator 将引发。如果您在可能为空的数据上使用它,请准备好try/except
  • @DhiaTN _ 不是 Python 中的特殊变量。它是一个正常的标识符,其作用与任何其他标识符一样。你说得对,它传统上用来说“我不关心这个值”,因为它对于 varname 来说看起来很不寻常,但这只是约定; Python 本身并没有特别对待它,这与 Go 等语言不同,其中 _ 被语言保留用于一次性标识符并且无法存储值。 (Python REPL 使用_ 存储最后一个值也与Python语言本身无关,只是另一个约定的例子)
【解决方案3】:

使用大小为 1 的 deque

from collections import deque

#aa is an interator
aa = iter('apple')

dd = deque(aa, maxlen=1)
last_element = dd.pop()

【讨论】:

  • 这实际上是耗尽长序列的最快方法,虽然只比 for 循环快一点。
  • +1 表示技术上是正确的,但读者应该有通常的 Python 警告,“你真的需要优化这个吗?”,“这不太明确,这不是 Pythonic”,以及“更快的速度取决于实现,这可能会发生变化。”
  • 另外,它是一个记忆猪
  • @EelcoHoogendoorn 为什么它是一个内存猪,即使 maxlen 为 1?
  • 从目前这里介绍的所有解决方案中,我发现这是最快且最节省内存的解决方案。
【解决方案4】:

如果__reversed__ 可用,可能值得使用

if hasattr(my_iter,'__reversed__'):
    last = next(reversed(my_iter))
else:
    for last in my_iter:
        pass

【讨论】:

    【解决方案5】:

    就这么简单:

    max(enumerate(the_iter))[1]
    

    【讨论】:

    • 哦,这很聪明。不是最有效或最易读的,但很聪明。
    • 所以只是大声思考...这是有效的,因为enumerate 返回(index, value) 喜欢:(0, val0), (1, val1), (2, val2)... 然后默认情况下max 在给定元组列表时,比较只有元组的第一个值,除非两个第一个值相等,它们永远不会在这里,因为它们代表索引。然后尾随下标是因为 max 返回整个 (idx, value) 元组,而我们只对value 感兴趣。有趣的想法。
    【解决方案6】:

    由于 lambda,这不太可能比空的 for 循环更快,但也许它会给其他人一个想法

    reduce(lambda x,y:y,my_iter)
    

    如果 iter 为空,则引发 TypeError

    【讨论】:

    • 恕我直言,从概念上讲,这个是最直接的。除了为空的迭代提升 TypeError 之外,您还可以通过 reduce() 的初始值提供默认值,例如 last = lambda iterable, default=None: reduce(lambda _, x: x, iterable, default)
    【解决方案7】:

    有这个

    list( the_iter )[-1]
    

    如果迭代的长度真的很长——长到实现列表会耗尽内存——那么你真的需要重新考虑设计。

    【讨论】:

    • 这是最直接的解决方案。
    • 使用元组稍微好一点。
    • 最后一句话强烈反对。使用非常大的数据集(如果一次加载可能会超出内存限制)是使用迭代器而不是列表的主要原因。
    • @Paul:有些函数只返回一个迭代器。在这种情况下,这是一种简短且易读的方法(对于非史诗列表)。
    • 这是一个应该避免作为坏坏坏习惯的最不有效的方法。另一种是使用 sort(sequence)[-1] 来获取序列的最大元素。如果您想成为软件工程师,请不要使用这些不良模式。
    【解决方案8】:

    我会使用reversed,除了它只接受序列而不是迭代器,这似乎相当随意。

    无论你怎么做,你都必须遍历整个迭代器。在最大效率下,如果您不再需要迭代器,您可以丢弃所有值:

    for last in my_iter:
        pass
    # last is now the last item
    

    不过,我认为这是一个次优的解决方案。

    【讨论】:

    • reversed() 不带迭代器,只带序列。
    • 这不是任意的。反转迭代器的唯一方法是迭代到最后,同时将所有项目保留在内存中。我,e,你需要先把它做成一个序列,然后才能反转它。这当然首先破坏了迭代器的目的,并且还意味着您突然无缘无故地使用了大量内存。所以事实上,它与任意相反。 :)
    • @Lennart - 当我说任意时,我的意思是烦人。我将把我的语言技能集中在我的论文上
    • 很公平。尽管 IMO 如果确实接受迭代器会更烦人,因为几乎任何使用它都是一个坏主意(tm)。 :)
    【解决方案9】:

    toolz 库提供了一个很好的解决方案:

    from toolz.itertoolz import last
    last(values)
    

    但添加非核心依赖项可能不值得仅在这种情况下使用它。

    【讨论】:

      【解决方案10】:

      查看此代码以获得类似的内容:

      http://excamera.com/sphinx/article-islast.html

      你可以用它来拿起最后一个项目:

      [(last, e) for (last, e) in islast(the_iter) if last]
      

      【讨论】:

      【解决方案11】:

      我只会使用next(reversed(myiter))

      【讨论】:

      • TypeError: reversed() 的参数必须是一个序列
      【解决方案12】:

      问题是关于获取迭代器的最后一个元素,但是如果您的迭代器是通过对序列应用条件来创建的,那么 reversed 可用于查找反向序列的“第一个”,只查看所需的元素, 通过对序列本身应用反向。

      一个人为的例子,

      >>> seq = list(range(10))
      >>> last_even = next(_ for _ in reversed(seq) if _ % 2 == 0)
      >>> last_even
      8
      

      【讨论】:

        【解决方案13】:

        也可以使用无限迭代器:

        from itertools import islice 
        last = list(islice(iterator(), 1000))[-1] # where 1000 is number of samples 
        

        我认为它会比 deque 慢,但它和 for 循环方法一样快并且实际上更快(不知何故)

        【讨论】:

          【解决方案14】:

          问题是错误的,只能导致复杂且低效的答案。 要获得迭代器,您当然要从可迭代的东西开始,这在大多数情况下会提供一种更直接的方式来访问最后一个元素。

          一旦你从一个可迭代对象创建了一个迭代器,你就会陷入遍历元素的困境,因为这是可迭代对象提供的唯一东西。

          因此,最有效和最清晰的方法不是一开始就创建迭代器,而是使用可迭代对象的本机访问方法。

          【讨论】:

          • 那么如何获取文件的最后一行?
          • @BriceM.Dempsey 最好的方法不是遍历整个(可能是巨大的)文件,而是将文件大小减去 100,读取最后 100 个字节,扫描其中的换行符,如果没有,请再返回 100 个字节,等等。您还可以根据您的情况增加后退大小。阅读海量行绝对不是最佳解决方案。
          猜你喜欢
          • 2013-12-04
          • 1970-01-01
          • 2015-09-09
          • 2015-11-29
          • 2021-12-13
          • 2014-05-22
          • 2023-03-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多