从Python迭代器获取最后一项的最简洁方法

Pet*_*ter 102 python iterator python-2.7 python-3.x

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

my_iter = iter(range(5))
Run Code Online (Sandbox Code Playgroud)

什么是最短的代码/最干净的方式4来自my_iter

我可以做到这一点,但它看起来效率不高:

[x for x in my_iter][-1]
Run Code Online (Sandbox Code Playgroud)

Tho*_*ers 90

item = defaultvalue
for item in my_iter:
    pass
Run Code Online (Sandbox Code Playgroud)

  • defaultvalue只是我示例的占位符.如果你想使用`None`作为默认值,那就是你的选择.没有一个并不总是最合理的默认,甚至可能不是带外的.我个人倾向于使用'defaultvalue = object()'来确保它是一个真正独特的值.我只是表明默认选择超出了本例范围. (42认同)
  • @ S.Lott:或许区分空迭代器和具有"无"的迭代器之间的差异是有用的,因为它的最终值 (27认同)
  • 所有内置容器类型的所有迭代器都存在设计错误?我第一次听说过它:) (8认同)
  • 虽然这可能是更快的解决方案,但它依赖于for循环中的变量泄漏(对某些人来说是一个特征,对其他人来说是一个错误 - 可能是FP-guys感到震惊).无论如何,Guido说这将始终以这种方式工作,因此使用它是安全的结构. (7认同)
  • 为什么占位符是"defaultvalue"?为什么不"无"?这正是"无"的意思.您是否建议某些功能特定的默认值甚至可能是正确的?如果迭代器实际上没有迭代,那么带外值比某些误导性函数特定的默认值更有意义. (4认同)
  • 这是一个更快的解决方案,它不是最快的。最快的是在 C 中执行 for 循环,根据 martin23487234 的后期答案使用`collections.deque` 来完成。类似于 [itertools recipes](http://docs.python.org/library/itertools.html#recipes) 中 `consume` 的工作方式。 (2认同)
  • 捕获NameError并不是一个好主意,因为您无法确定会捕获到哪个* NameError。您最终可能会将错误隐藏在迭代器本身中。 (2认同)

小智 58

使用deque1号.

from collections import deque

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

dd = deque(aa, maxlen=1)
last_element = dd.pop()
Run Code Online (Sandbox Code Playgroud)

  • +1在技术上是正确的,但读者应该有通常的Python警告,"你真的需要优化它吗?","这是不太明确,这不是Pythonic","和更快的速度取决于实施,可能会改变." (11认同)
  • 这实际上是耗尽长序列的最快方法,尽管只比for循环更快. (6认同)
  • @EelcoHoogendoorn为什么即使maxlen为1,它也是一个记忆猪? (6认同)
  • 到目前为止,从这里介绍的所有解决方案中,我发现这是最快且内存效率最高的解决方案。 (3认同)

Dhi*_*aTN 55

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

*_, last = iterator # for a better understanding check PEP 448
print(last)
Run Code Online (Sandbox Code Playgroud)

如果您使用的是python 2.7:

last = next(iterator)
for last in iterator:
    continue
print last
Run Code Online (Sandbox Code Playgroud)


注意:

通常上面提到的解决方案是常规案例所需的解决方案,但如果您处理大量数据,则使用deque大小为1 的解决方案效率更高.(来源)

from collections import deque

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

dd = deque(aa, maxlen=1)
last_element = dd.pop()
Run Code Online (Sandbox Code Playgroud)

  • 如果 `iterator` 为空,`*_, last = iterator` 将引发。如果您在可能为空的数据上使用它,请准备好“try”/“ except”。 (7认同)
  • Python 3 解决方案的内存效率不高。 (4认同)
  • @virtualxtc nope`_`是python中的特殊变量,用于存储最后一个值,或者说我不在乎该值,因此可以清除。 (3认同)
  • @DhiaTN `_` 不是 Python 中的特殊变量。它是一个正常的标识符,其作用与其他标识符一样。你是对的,**传统上**用来表示“我不关心这个值”,因为它对于 varname 来说看起来很不寻常,但这只是惯例;Python 本身并没有对它进行特殊处理,这与 Go 等语言不同,其中“_”被该语言保留为一次性标识符,并且无法存储值。(Python REPL 使用 _ 来存储最后一个值也与 Python 语言本身无关,这只是约定的另一个示例) (3认同)
  • @virtualxtc:下划线只是一个标识符。前面的星星表示“展开列表”。更具可读性的是“*lst, last = some_iterable”。 (2认同)
  • @DhiaTN是的,您绝对正确。实际上,我喜欢您展示了很多的Python 3习惯用法。我只是想说明一下,它不适用于“大数据”。我为此使用collections.deque,它恰好快速且具有内存效率(请参阅martin23487234的解决方案)。 (2认同)
  • 理解这一点很重要,因为它解释了 Markus Strauss 的断言:3.x 示例内存效率低下。看,如果“_”实际上是规范规定的一次性标识符,那么“*_,last = some_huge_iterator()”无论迭代器有多大,都可以完全正常运行。一次性值将在生成后立即被丢弃,并且永远不会出现。但是因为“_”只是一个普通的标识符,所以该片段所做的就是构造一个大的旧值列表并将其全部存储在名为“_”的变量中。这就是为什么它最终会在“大数据”上使用大量内存。 (2认同)

Joh*_*ooy 32

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

if hasattr(my_iter,'__reversed__'):
    last = next(reversed(my_iter))
else:
    for last in my_iter:
        pass
Run Code Online (Sandbox Code Playgroud)


小智 25

很简单:

max(enumerate(the_iter))[1]
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*ooy 19

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

reduce(lambda x,y:y,my_iter)
Run Code Online (Sandbox Code Playgroud)

如果iter为空,则引发TypeError


S.L*_*ott 9

就是这个

list( the_iter )[-1]
Run Code Online (Sandbox Code Playgroud)

如果迭代的长度真的是史诗般的 - 只要实现列表会耗尽内存 - 那么你真的需要重新考虑设计.

  • 非常不同意最后一句话.使用非常大的数据集(如果一次加载可能超过内存边界)是使用迭代器而不是列表的主要原因. (8认同)
  • 这是最直接的解决方案。 (2认同)
  • 使用元组更好. (2认同)
  • 这是人们应该避免的最有效的方法,也是一种坏习惯。另一种是使用 sort(sequence)[-1] 来获取序列的最大元素。如果你想成为软件工程师,请永远不要使用这些不良模式。 (2认同)

Chr*_*utz 5

我将使用reversed,只是它只使用序列而不是迭代器,这似乎相当武断。

无论采用哪种方式,都必须遍历整个迭代器。以最高的效率,如果您不再需要迭代器,则可以废弃所有值:

for last in my_iter:
    pass
# last is now the last item
Run Code Online (Sandbox Code Playgroud)

我认为这是次佳的解决方案。

  • reversed()不需要迭代器,只需序列即可。 (3认同)
  • 这一点也不随意。逆转迭代器的唯一方法是迭代到最后,同时将所有项目保留在内存中。我,例如,您需要先对其进行排序,然后才能将其反转。当然,这首先会破坏迭代器的目的,也意味着您会突然无故浪费大量内存。因此,实际上,这与任意性相反。:) (3认同)
  • 很公平。尽管IMO确实接受迭代器会更令人讨厌,因为几乎任何使用它都将是一个坏主意(tm)。:) (3认同)