简体   繁体   中英

Python looping: idiomatically comparing successive items in a list

I need to loop over a list of objects, comparing them like this: 0 vs. 1, 1 vs. 2, 2 vs. 3, etc. (I'm using pysvn to extract a list of diffs.) I wound up just looping over an index, but I keep wondering if there's some way to do it which is more closely idiomatic. It's Python; shouldn't I be using iterators in some clever way? Simply looping over the index seems pretty clear, but I wonder if there's a more expressive or concise way to do it.

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

This is called a sliding window. There's an example in the itertools documentation that does it. Here's the code:

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

What that, you can say this:

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

Of course you only care about the case where n=2, so you can get away with something much simpler:

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)

I'd probably do:

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)

Something similarly cleverer and not touching the iterators themselves could probably be done using

So many complex solutions posted, why not keep it simple?

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

Store the previous value in a variable. Initialize the variable with a value you're not likely to find in the sequence you're handling, so you can know if you're at the first element. Compare the old value to the current value.

Reduce can be used for this purpose, if you take care to leave a copy of the current item in the result of the reducing function .

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, [])

EDIT: Yes, but nobody said the result of the function in reduce has to be scalar. I changed my example to use a list. Basically, the last element is allways the previous revision (except on first pass), with all preceding elements being the results of the svn.diff_summarize call. This way, you get a list of results as your final output...

EDIT2: Yep, the code really was broken. I have here a workable dummy:

>>> 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']

This was tested in the shell, as you can see. You will want to replace (prev, nxt) in the lst.append call of compare to actually append the summary of the call to 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM