【问题标题】:Python looping: idiomatically comparing successive items in a listPython循环:惯用地比较列表中的连续项目
【发布时间】:2011-01-10 07:01:12
【问题描述】:

我需要遍历一个对象列表,像这样比较它们:0 与 1、1 与 2、2 与 3 等(我正在使用 pysvn 提取差异列表。)我结束只是循环一个索引,但我一直想知道是否有某种方法可以做到这一点更接近地道。它是 Python;我不应该以某种聪明的方式使用迭代器吗?简单地循环索引似乎很清楚,但我想知道是否有更表达或更简洁的方式来做到这一点。

for revindex in xrange(len(dm_revisions) - 1):
    summary = \
        svn.diff_summarize(svn_path,
                          revision1=dm_revisions[revindex],
                          revision2 = dm_revisions[revindex+1])

【问题讨论】:

  • 就我个人而言,我觉得实际上可能存在更聪明的方法来做到这一点,但循环索引是最清晰的方法。
  • +1 以获得良好的描述,让我也找到了解决方案。

标签: loops iterator python


【解决方案1】:

将前一个值存储在一个变量中。使用您在处理的序列中不太可能找到的值初始化变量,这样您就可以知道您是否在第一个元素处。将旧值与当前值进行比较。

【讨论】:

  • 啊,这听起来像是一种有趣的替代方法——虽然不像创建一个花哨的成对迭代器那样 Python :)
  • 实际上,一个花哨的成对迭代器会更像 Haskellish/Lispish,尽管它可以在 Python 中工作。
  • 有趣;我想我还有更多关于这三种表达方式的知识。
【解决方案2】:

我可能会这样做:

import itertools
for rev1, rev2 in zip(dm_revisions, itertools.islice(dm_revisions, 1, None)):
    summary = svn.diff_sumeraize(svn_python, revision1=rev, revision2=rev2)

类似的更聪明且不涉及迭代器本身的东西可能可以使用

【讨论】:

  • 这是我想到的第一件事,它是更实用的方法。实际上,您正在使用“其余部分”本身压缩列表(导致 v1,v2,v2,v3,v3...),然后从结果列表 (v1,v2)(v2, v3)(v3,v4)...
  • 有道理,而且看起来很简洁。使用 izip 怎么样,如下所述:docs.python.org/library/itertools.html ?
【解决方案3】:

如果您注意在缩减函数的结果中保留当前项目的副本,则可以使用缩减来实现此目的。

def diff_summarize(revisionList, nextRevision):
    '''helper function (adaptor) for using svn.diff_summarize with reduce'''
    if revisionList:
        # remove the previously tacked on item
        r1 = revisionList.pop()
        revisionList.append(svn.diff_summarize(
            svn_path, revision1=r1, revision2=nextRevision))
    # tack the current item onto the end of the list for use in next iteration
    revisionList.append(nextRevision)
    return revisionList

summaries = reduce(diff_summarize, dm_revisions, [])

编辑: 是的,但没有人说reduce 中的函数结果必须是标量。我将示例更改为使用列表。基本上,最后一个元素总是之前的版本(除了第一次通过),所有前面的元素都是svn.diff_summarize 调用的结果。这样,您将获得一个结果列表作为最终输出...

EDIT2:是的,代码确实被破坏了。我这里有一个可行的假人:

>>> def compare(lst, nxt):
...    if lst:
...       prev = lst.pop()
...       lst.append((prev, nxt))
...    lst.append(nxt)
...    return lst
...
>>> reduce(compare, "abcdefg", [])
[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e'), ('e', 'f'), ('f', 'g'), 'g']

如您所见,这是在 shell 中测试的。您需要在 comparelst.append 调用中替换 (prev, nxt) 以实际将调用摘要附加到 svn.diff_summarize

>>> help(reduce)
Help on built-in function reduce in module __builtin__:

reduce(...)
    reduce(function, sequence[, initial]) -> value

    Apply a function of two arguments cumulatively to the items of a sequence,
    from left to right, so as to reduce the sequence to a single value.
    For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
    ((((1+2)+3)+4)+5).  If initial is present, it is placed before the items
    of the sequence in the calculation, and serves as a default when the
    sequence is empty.

【讨论】:

  • 不,reduce 将函数应用于序列中的每个元素以及到目前为止累积的缩减值,而不是将其应用于每个元素及其前身。
  • 我相信 OP 只是想比较连续的元素。 reduce 所做的是对前两个元素进行操作,获取结果,然后对结果和下一个元素执行操作,并重复此操作,直到没有剩余元素为止。
  • 当然,但这只是略有不同——您仍在将一次迭代的数据与下一次迭代的数据进行比较。查看更新的代码。
  • 该代码看起来很糟糕。您可以为此使用reduce:pastie.org/798394,但我不推荐它。它似乎不必要地不透明。
【解决方案4】:

这称为滑动窗口。有一个example in the itertools documentation 可以做到这一点。代码如下:

from itertools import islice

def window(seq, n=2):
    "Returns a sliding window (of width n) over data from the iterable"
    "   s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ...                   "
    it = iter(seq)
    result = tuple(islice(it, n))
    if len(result) == n:
        yield result    
    for elem in it:
        result = result[1:] + (elem,)
        yield result

那是什么,你可以这样说:

for r1, r2 in window(dm_revisions):
    summary = svn.diff_summarize(svn_path, revision1=r1, revision2=r2)

当然,您只关心 n=2 的情况,因此您可以使用更简单的方法:

def adjacent_pairs(seq):
    it = iter(seq)
    a = it.next()
    for b in it:
        yield a, b
        a = b

for r1, r2 in adjacent_pairs(dm_revisions):
    summary = svn.diff_summarize(svn_path, revision1=r1, revision2=r2)

【讨论】:

  • 我看到较新的 itertools 文档在“食谱”部分 (docs.python.org/library/itertools.html) 中有一个“成对”功能。看起来它会做同样的事情,是吗?
  • 是的。 (谢天谢地,我们有 15 个字符的限制。否则我只能回答“是”或“否”问题。)
  • 太棒了。这行得通,而且我认为更清楚。我可以给修订提供信息性名称,以便人们知道脚本中进一步使用的内容。我也很高兴看到所有内容都被拼写出来,即使我最终使用了“tee”和“izip”。
【解决方案5】:

发布了这么多复杂的解决方案,为什么不保持简单呢?

myList = range(5)

for idx, item1 in enumerate(myList[:-1]):
    item2 = L[idx + 1]
    print item1, item2

>>> 
0 1
1 2
2 3
3 4

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 2020-06-08
    • 2018-10-04
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多