Pairwise circular Python 'for' loop

后端 未结 18 847
执念已碎
执念已碎 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:57

    Pairwise circular Python 'for' loop

    If you like the accepted answer,

    zip(L, L[1:] + L[:1])
    

    you can go much more memory light with semantically the same code using itertools:

    from itertools import islice, chain #, izip as zip # uncomment if Python 2
    

    And this barely materializes anything in memory beyond the original list (assuming the list is relatively large):

    zip(l, chain(islice(l, 1, None), islice(l, None, 1)))
    

    To use, just consume (for example, with a list):

    >>> list(zip(l, chain(islice(l, 1, None), islice(l, None, 1))))
    [(1, 2), (2, 3), (3, 1)]
    

    This can be made extensible to any width:

    def cyclical_window(l, width=2):
        return zip(*[chain(islice(l, i, None), islice(l, None, i)) for i in range(width)])
    

    and usage:

    >>> l = [1, 2, 3, 4, 5]
    >>> cyclical_window(l)
    
    >>> list(cyclical_window(l))
    [(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
    >>> list(cyclical_window(l, 4))
    [(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 1), (4, 5, 1, 2), (5, 1, 2, 3)]
    

    Unlimited generation with itertools.tee with cycle

    You can also use tee to avoid making a redundant cycle object:

    from itertools import cycle, tee
    ic1, ic2 = tee(cycle(l))
    next(ic2)    # must still queue up the next item
    

    and now:

    >>> [(next(ic1), next(ic2)) for _ in range(10)]
    [(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
    

    This is incredibly efficient, an expected usage of iter with next, and elegant usage of cycle, tee, and zip.

    Don't pass cycle directly to list unless you have saved your work and have time for your computer to creep to a halt as you max out its memory - if you're lucky, after a while your OS will kill the process before it crashes your computer.

    Pure Python Builtin Functions

    Finally, no standard lib imports, but this only works for up to the length of original list (IndexError otherwise.)

    >>> [(l[i], l[i - len(l) + 1]) for i in range(len(l))]
    [(1, 2), (2, 3), (3, 1)]
    

    You can continue this with modulo:

    >>> len_l = len(l)
    >>> [(l[i % len_l], l[(i + 1) % len_l]) for i in range(10)]
    [(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
    

提交回复
热议问题