2014-09-13 2 views
2

Если я:Есть ли способ в python изменить порядок обработки доходности get?

def foo(): 
    yield from range(0,10) 
    yield from range(10,20) 

for num in foo(): 
    print(num) 

Я получаю упорядоченный список от 0 до 19. без изменения ввода функций диапазона, есть простой способ определить, что я хочу список, который идет: 0, 10,1,11,2,12 ...

В основном я сначала хочу первый элемент каждого генератора. Чем я хочу второй элемент каждого генератора, а затем третий и т. Д.

Бонусные баллы: Есть ли способ изменить его так, чтобы, когда генераторы производят неравное количество результатов, второй генератор дает остальные результаты после того, как первый закончен?

ответ

7

Вы пытаетесь использовать итераторы zip(); сделать это явно:

from itertools import chain 

def foo(): 
    yield from chain.from_iterable(zip(range(10), range(10, 20))) 

Использование itertools.chain.from_iterable() позволяет продолжать использовать yield from здесь, уплощение кортежи zip() производит.

Демо:

>>> from itertools import chain 
>>> def foo(): 
...  yield from chain.from_iterable(zip(range(10), range(10, 20))) 
... 
>>> list(foo()) 
[0, 10, 1, 11, 2, 12, 3, 13, 4, 14, 5, 15, 6, 16, 7, 17, 8, 18, 9, 19] 

Если у вас есть генераторы различной длины, можно использовать itertools.zip_longest():

from itertools import zip_longest 

def foo(): 
    yield from (i for pair in zip_longest(range(10), range(10, 22)) 
        for i in pair if i is not None) 

Я использовал другую технику, уплощение здесь с двойной петлей в выражении генератора.

Это все действительно становится утомительным, и так как вы не используете yield from с другим генератором (так что вам не нужна поддержка generator.send() и generator.throw() быть распространяемый), вы можете также просто сделать это правильный цикл:

def foo(): 
    for x, y in zip_longest(range(10), range(10, 22)): 
     if x is not None: 
      yield x 
     if y is not None: 
      yield y    

Вы можете также использовать roundrobin() рецепт, указанный в itertools documentation recipies section:

from itertools import cycle 

def roundrobin(*iterables): 
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C" 
    # Recipe credited to George Sakkis 
    pending = len(iterables) 
    nexts = cycle(iter(it).__next__ for it in iterables) 
    while pending: 
     try: 
      for next in nexts: 
       yield next() 
     except StopIteration: 
      pending -= 1 
      nexts = cycle(islice(nexts, pending)) 

def foo(): 
    yield from roundrobin(range(10), range(10, 22)) 
+0

Что делать, если генераторы имеют различную длину? Это решение, похоже, молча забывает остальную цепь. Я бы предпочел, чтобы все остальное. – Christian

+1

@Christian: используйте ['itertools.zip_longest'] (https://docs.python.org/3/library/itertools.html#itertools.zip_longest), возможно, отфильтровывая значения' None'. Но вам придется делать это * явно *. –

+0

@MartijnPieters Я думаю, что я сказал это раньше¹, но ... «roundrobin» из документов, может быть? ¹http: //stackoverflow.com/a/23874310/1763356 – Veedrac