14

I know you can use c = cycle(['a', 'b', 'c']) to cycle between the elements using c.next(), but is there away to get the iterator's current element?

for example if c.next() returned 'c', it means that iterator was at 'b' before. Is there a way I can get 'b' without using next()?

cs95
  • 379,657
  • 97
  • 704
  • 746
Algorithmatic
  • 1,824
  • 2
  • 24
  • 41

2 Answers2

5

Iterators/generators don't have any way to get the current value. You should either keep a reference to it or create some wrapper that holds onto it for you.

cs95
  • 379,657
  • 97
  • 704
  • 746
2

Note: This only works if there are no repeated elements. Whether this is a serious practical limitation depends on each one's use. In my case, most of the itertools.cycle I have worked with fall under this category.

One can actually get the current state of a cycle with a helper function, and other info as well. It actually uses next, but this is transparent to the caller.

import itertools 

def get_cycle_props(cycle) :
    """Get the properties (elements, length, current state) of a cycle, without advancing it"""
    # Get the current state
    partial = []
    n = 0
    g = next(cycle)
    while ( g not in partial ) :
        partial.append(g)
        g = next(cycle)
        n += 1
    # Cycle until the "current" (now previous) state
    for i in range(n-1) :
        g = next(cycle)
    return (partial, n, partial[0])

def get_cycle_list(cycle) :
    """Get the elements of a cycle, without advancing it"""
    return get_cycle_props(cycle)[0]

def get_cycle_state(cycle) :
    """Get the current state of a cycle, without advancing it"""
    return get_cycle_props(cycle)[2]

def get_cycle_len(cycle) :
    """Get the length of a cycle, without advancing it"""
    return get_cycle_props(cycle)[1]

# initialize list 
test_list = [3, 4, 5, 7, 1] 
c = itertools.cycle(test_list)
print('cycle state =', get_cycle_state(c))
print('cycle length =', get_cycle_len(c))
print('cycle list =', get_cycle_list(c))
next(c)
print('cycle state =', get_cycle_state(c))
print('cycle length =', get_cycle_len(c))
print('cycle list =', get_cycle_list(c))

produces the following output

cycle state = 3
cycle length = 5
cycle list = [3, 4, 5, 7, 1]
cycle state = 4
cycle length = 5
cycle list = [4, 5, 7, 1, 3]

This can actually be taken advantage of to "rewind" a cycle, with the function

def shift_cycle(cycle, npos=0) :
    """Shift a cycle, a given number of positions (can be negative)."""
    (cycle_list, nelem, curr_state) = get_cycle_props(cycle)
    for i in range(nelem+npos) :
        g = next(cycle)
    return

Try

shift_cycle(c, -2)
print('cycle state =', get_cycle_state(c))
  • 1
    The "Note" at the bottom should be highlighted a little more prominently imo, that's a pretty large caveat. This will fail for something as simple as `[1, 1, 2]`. – cs95 Dec 22 '19 at 18:34
  • @cs95 - Already emphasized... Whether this is a serious practical limitation depends on each one's use. In my case, most of the `itertools.cycle` I have worked with fall under this category, so it would work neatly. – sancho.s ReinstateMonicaCellio Dec 23 '19 at 00:45