Pairwise circular Python 'for' loop

后端 未结 18 830
执念已碎
执念已碎 2020-12-24 10:24

Is there a nice Pythonic way to loop over a list, retuning a pair of elements? The last element should be paired with the first.

So for instance, if I have the list

相关标签:
18条回答
  • 2020-12-24 10:39

    If you don't want to consume too much memory, you can try my solution:

    [(l[i], l[(i+1) % len(l)]) for i, v in enumerate(l)]

    It's a little slower, but consume less memory.

    0 讨论(0)
  • 2020-12-24 10:41

    There are more efficient ways (that don't built temporary lists), but I think this is the most concise:

    > l = [1,2,3]
    > zip(l, (l+l)[1:])
    [(1, 2), (2, 3), (3, 1)]
    
    0 讨论(0)
  • 2020-12-24 10:41

    L = [1, 2, 3] a = zip(L, L[1:]+L[:1]) for i in a: b = list(i) print b

    0 讨论(0)
  • 2020-12-24 10:43

    I would pair itertools.cycle with zip:

    import itertools
    
    def circular_pairwise(l):
        second = itertools.cycle(l)
        next(second)
        return zip(l, second)
    

    cycle returns an iterable that yields the values of its argument in order, looping from the last value to the first.

    We skip the first value, so it starts at position 1 (rather than 0).

    Next, we zip it with the original, unmutated list. zip is good, because it stops when any of its argument iterables are exhausted.

    Doing it this way avoids the creation of any intermediate lists: cycle holds a reference to the original, but doesn't copy it. zip operates in the same way.

    It's important to note that this will break if the input is an iterator, such as a file, (or a map or zip in python-3), as advancing in one place (through next(second)) will automatically advance the iterator in all the others. This is easily solved using itertools.tee, which produces two independently operating iterators over the original iterable:

    def circular_pairwise(it):
        first, snd = itertools.tee(it)
        second = itertools.cycle(snd)
        next(second)
        return zip(first, second)
    

    tee can use large amounts of additional storage, for example, if one of the returned iterators is used up before the other is touched, but as we only ever have one step difference, the additional storage is minimal.

    0 讨论(0)
  • 2020-12-24 10:43

    I like a solution that does not modify the original list and does not copy the list to temporary storage:

    def circular(a_list):
        for index in range(len(a_list) - 1):
            yield a_list[index], a_list[index + 1]
        yield a_list[-1], a_list[0]
    
    for x in circular([1, 2, 3]):
        print x
    

    Output:

    (1, 2)
    (2, 3)
    (3, 1)
    

    I can imagine this being used on some very large in-memory data.

    0 讨论(0)
  • 2020-12-24 10:47

    I would use a deque with zip to achieve this.

    >>> from collections import deque
    >>>
    >>> l = [1,2,3]
    >>> d = deque(l)
    >>> d.rotate(-1)
    >>> zip(l, d)
    [(1, 2), (2, 3), (3, 1)]
    
    0 讨论(0)
提交回复
热议问题