【问题标题】:pairwise traversal of a list or tuple列表或元组的成对遍历
【发布时间】:2017-11-02 14:37:59
【问题描述】:
a = [5, 66, 7, 8, 9, ...]

是否可以进行迭代而不是这样写?

a[1] - a[0]

a[2] - a[1]

a[3] - a[2]

a[4] - a[3]

...

谢谢!

【问题讨论】:

标签: python iteration


【解决方案1】:

可以使用range。然而,编程(如数学)是建立在抽象之上的。连续对[(x0, x1), (x1, x2), ..., (xn-2, xn-1)],称为成对组合。请参阅itertools docs 中的示例。一旦你的工具集中有了这个函数,你就可以编写:

for x, y in pairwise(xs):
    print(y - x)

或用作生成器表达式:

consecutive_diffs = (y - x for (x, y) in pairwise(xs))

【讨论】:

  • Python 3.9.7 >>> from itertools import pairwise Traceback(最近一次调用最后一次):文件“”,第 1 行,在 ImportError: cannot import name 'pairwise' from 'itertools'(未知位置)
  • pairwise 已从(晚于 3.1 左右)移至收据。根据官方文档使用 zip 和 tee。
【解决方案2】:

对于 python 2 中的小列表或 python 3 中的任何列表,您可以使用

[x - y for x, y in zip(a[1:], a)]

对于更大的列表,您可能需要

import itertools as it

[x - y for x, y in it.izip(a[1:], a)]

如果你使用的是 python 2

我会考虑把它写成生成器表达式

(x - y for x, y in it.izip(a[1:], a))

这将避免一次在内存中创建第二个列表,但您只能对其进行一次迭代。如果您只想要对其进行一次迭代,那么这是理想的,并且如果您稍后决定需要随机或重复访问,则很容易更改。特别是如果您要进一步处理它以制作列表,那么最后一个选项是理想的。

更新:

目前最快的方法是

import itertools as it
import operator as op

list(it.starmap(op.sub, it.izip(a[1:], a)))

$ python -mtimeit -s's = [1, 2]*10000' '[x - y for x, y in zip(s[1:], s)]'
100 loops, best of 3: 13.5 msec per loop

$ python -mtimeit -s'import itertools as it; s = [1, 2]*10000' '[x - y for x, y in it.izip(s[1:], s)]'
100 loops, best of 3: 8.4 msec per loop

$ python -mtimeit -s'import itertools as it; import operator as op; s = [1, 2]*10000' 'list(it.starmap(op.sub, it.izip(s[1:], s)))'
100 loops, best of 3: 6.38 msec per loop

【讨论】:

  • 对于一项微不足道的任务,它们实际上看起来过于复杂,尤其是考虑到 OP 的假设经验。普通的 for 循环什么时候变得不好用了?
  • 当阅读变得比理解慢两倍时。
  • 好的,我进行了分析,在这种情况下,它们的速度不是两倍,但它们仍然是迄今为止最慢的。 16.8 毫秒,输入与上面显示的相同。我关于它们阅读起来更粗鲁的观点仍然成立。
  • @Ivo:迭代索引而不是数组元素也会引入额外的错误可能性。例如,如果你不小心让你的 for 循环从 0 而不是 1 开始,你会得到一个 index out of bounds 错误。使用列表推导和迭代工具不必担心这一点。
  • 尽管迭代索引存在错误风险,但它是迄今为止 pypy 2.0 中最快的(每 250 微秒,其他为 400-600 微秒):pypy -mtimeit -s'import itertools;import operator; seq=[0,1]*10000' '[seq[i+1] - seq[i] for i in range(len(seq)-1)]'
【解决方案3】:

我建议使用很棒的more_itertools 库,它有现成的pairwise function

import more_itertools

for a, b in more_itertools.pairwise([1, 2, 3, 4, 5]):
    print(a, b)
# 1 2
# 2 3
# 3 4
# 4 5

这将使您免于编写自己的(可能是错误的)实现。例如,此页面上的大多数实现都不能正确处理空迭代的情况——生成器函数永远不应该引发StopIteration,这种行为considered deprecated 并导致 Python 3.6 中的 DeprecationWarning。它根本不适用于 Python 3.7。

【讨论】:

  • 在 Python 3.10 中,pairwise function 已添加到标准库的一部分 itertools
【解决方案4】:

这是来自 itertools 食谱的示例:

from itertools import tee

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

这不是很可读。如果你喜欢更容易理解的东西 了解生成器是如何工作的,这里有一个更长的例子,结果相同:

def pairwise(it):
    """
    Walk a list in overlapping pairs.

    >>> list(pairwise([0, 1, 2, 3, 4, 5]))
    [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
    """
    it = iter(it)
    start = None
    while True:
        if not start:
            start = next(it)
        end = next(it)
        yield start, end
        start = end

【讨论】:

    【解决方案5】:

    当然。

    for i in range(1, len(a)):
        print a[i] - a[i-1]
    

    我看不出真正的问题是什么。你读过the python tutorial吗?

    【讨论】:

      【解决方案6】:
      def pairwise(iterable):
          i = iter(iterable)
          while True:
              yield next(i), next(i, '')
      

      【讨论】:

      • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
      • -1。对于[1, 2, 3, 4, 5, 6],这会产生[(1, 2), (3, 4), (5, 6)]。这个问题想要更接近[(1, 2), (2, 3), (3, 4), ...]
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-16
      • 2013-04-07
      • 1970-01-01
      • 2019-03-17
      • 1970-01-01
      相关资源
      最近更新 更多