Python now, next, n iteration

writes a general function that can iterate over any iterable, returning now, to the following pairs.

def now_nxt(iterable): iterator = iter(iterable) nxt = iterator.__next__() for x in iterator: now = nxt nxt = x yield (now,nxt) for i in now_nxt("hello world"): print(i) ('h', 'e') ('e', 'l') ('l', 'l') ('l', 'o') ('o', ' ') (' ', 'w') ('w', 'o') ('o', 'r') ('r', 'l') ('l', 'd') 

I was thinking about how best to write a function in which you can set the number of elements in each tuple.

for example if he was

 func("hello",n=3) 

the result will be:

 ('h','e','l') ('e','l','l') ('l','l','o') 

I am new to using timeit, so please indicate that I am doing something wrong:

 import timeit def n1(iterable, n=1): #now_nxt_deque from collections import deque deq = deque(maxlen=n) for i in iterable: deq.append(i) if len(deq) == n: yield tuple(deq) def n2(sequence, n=2): # now_next from itertools import tee iterators = tee(iter(sequence), n) for i, iterator in enumerate(iterators): for j in range(i): iterator.__next__() return zip(*iterators) def n3(gen, n=2): from itertools import tee, islice gens = tee(gen, n) gens = list(gens) for i, gen in enumerate(gens): gens[i] = islice(gens[i], i, None) return zip(*gens) def prin(func): for x in func: yield x string = "Lorem ipsum tellivizzle for sure ghetto, consectetuer adipiscing elit." print("func 1: %f" %timeit.Timer("prin(n1(string, 5))", "from __main__ import n1, string, prin").timeit(100000)) print("func 2: %f" %timeit.Timer("prin(n2(string, 5))", "from __main__ import n2, string, prin").timeit(100000)) print("func 3: %f" %timeit.Timer("prin(n3(string, 5))", "from __main__ import n3, string, prin").timeit(100000)) 

results:

 $ py time_this_function.py func 1: 0.163129 func 2: 2.383288 func 3: 1.908363 
+6
source share
5 answers

My suggestion would be

 from collections import deque def now_nxt_deque(iterable, n=1): deq = deque(maxlen=n) for i in iterable: deq.append(i) if len(deq) == n: yield tuple(deq) for i in now_nxt_deque("hello world", 3): print(i) ('h', 'e', 'l') ('e', 'l', 'l') ('l', 'l', 'o') ('l', 'o', ' ') ('o', ' ', 'w') (' ', 'w', 'o') ('w', 'o', 'r') ('o', 'r', 'l') ('r', 'l', 'd') 
+5
source

Here's a really easy way to do this:

  • Clone your iterator n times using itertools.tee
  • Promote the ith iterator i times
  • izip all together
 import itertools def now_next(sequence, n=2): iterators = itertools.tee(iter(sequence), n) for i, iterator in enumerate(iterators): for j in range(i): iterator.next() return itertools.izip(*iterators) 
+5
source

My decision:

 def nn(itr, n): iterable = iter(itr) last = tuple(next(iterable, None) for _ in xrange(n)) yield last for _ in xrange(len(itr)): last = tuple(chain(last[1:], [next(iterable)])) yield last 

This was done for Python 2, if you want to use it with Python 3, replace xrange with range .

next , has an excellent default parameter that will be returned instead of raising StopIteration , you can also add this default parameter to your function as follows:

 def nn(itr, n, default=None): iterable = iter(itr) last = tuple(next(iterable, default) for _ in xrange(n)) yield last for _ in xrange(len(itr)): last = tuple(chain(last[1:], [next(iterable, default)])) yield last 

I played with him a little more, for example. using itr.__class__() by default, but this seems wrong for lists and tuples, so that just makes sense for strings.

+2
source

Eric's variant of the method that uses a slice

 from itertools import tee, islice, izip def now_next(gen, n=2): gens = tee(gen, n) gens = list(gens) for i, gen in enumerate(gens): gens[i] = islice(gens[i], i, None) return izip(*gens) for x in now_next((1,2,3,4,5,6,7)): print x 
+1
source

One liner based on the answer to cravoori:

 from itertools import tee, islice, izip def now_next(gen, n=2): return izip(*(islice(g, i, None) for i, g in enumerate(tee(gen, n)))) 
0
source

Source: https://habr.com/ru/post/918797/


All Articles