28

listaとitertools.countobject (つまり、無限値ジェネレーター) の2 つの反復子があります。これら 2 つをマージして、2 つの yield 値を交互に返すイテレータを作成したいと思います。

>>> import itertools
>>> c = itertools.count(1)
>>> items = ['foo', 'bar']
>>> merged = imerge(items, c)  # the mythical "imerge"
>>> merged.next()
'foo'
>>> merged.next()
1
>>> merged.next()
'bar'
>>> merged.next()
2
>>> merged.next()
Traceback (most recent call last):
    ...
StopIteration

これを行う最も簡単で簡潔な方法は何ですか?

4

13 に答える 13

45

ジェネレーターはあなたの問題をうまく解決します。

def imerge(a, b):
    for i, j in itertools.izip(a,b):
        yield i
        yield j
于 2008-10-28T16:14:02.387 に答える
16

@Pramodが最初に提案したこととほぼ同じことを行うことができます。

def izipmerge(a, b):
  for i, j in itertools.izip(a,b):
    yield i
    yield j

このアプローチの利点は、a と b の両方が無限大の場合にメモリ不足にならないことです。

于 2008-10-28T16:59:35.040 に答える
13

itertools が必要ないことにも同意します。

しかし、なぜ 2 で停止するのでしょうか。

  def tmerge(*iterators):
    for values in zip(*iterators):
      for value in values:
        yield value

0 以上の任意の数の反復子を処理します。

更新:DOH!コメンターは、すべての反復子が同じ長さでない限り、これは機能しないと指摘しました。

正しいコードは次のとおりです。

def tmerge(*iterators):
  empty = {}
  for values in itertools.izip_longest(*iterators, fillvalue=empty):
    for value in values:
      if value is not empty:
        yield value

はい、長さが等しくないリストと {} を含むリストで試してみました。

于 2008-12-05T22:39:29.963 に答える
12

私はこのようなことをします。オブジェクトを一緒に圧縮するオーバーヘッドがないため、これは時間とスペースの効率が最も高くなります。aこれは、とbが無限である場合にも機能します。

def imerge(a, b):
    i1 = iter(a)
    i2 = iter(b)
    while True:
        try:
            yield i1.next()
            yield i2.next()
        except StopIteration:
            return
于 2008-10-28T16:12:19.597 に答える
10

zipと同様に使用できますitertools.chain。これは、最初のリストが有限である場合にのみ機能します。

merge=itertools.chain(*[iter(i) for i in zip(['foo', 'bar'], itertools.count(1))])
于 2008-10-28T16:15:15.707 に答える
5

私は、はるかに簡潔なこの別の方法を好みます。

iter = reduce(lambda x,y: itertools.chain(x,y), iters)
于 2011-03-23T01:48:00.413 に答える
4

Python のあまり知られていない機能の 1 つは、ジェネレーター式に f​​or 句を追加できることです。zip()/izip() から取得したリストなど、ネストされたリストをフラット化するのに非常に便利です。

def imerge(*iterators):
    return (value for row in itertools.izip(*iterators) for value in row)
于 2011-03-30T14:09:21.860 に答える
3

あなたのアプリケーションが何かはわかりませんが、enumerate() 関数の方が便利かもしれません。

>>> items = ['foo', 'bar', 'baz']
>>> for i, item in enumerate(items):
...  print item
...  print i
... 
foo
0
bar
1
baz
2
于 2008-10-28T22:03:08.500 に答える
3

エレガントなソリューションは次のとおりです。

def alternate(*iterators):
    while len(iterators) > 0:
        try:
            yield next(iterators[0])
            # Move this iterator to the back of the queue
            iterators = iterators[1:] + iterators[:1]
        except StopIteration:
            # Remove this iterator from the queue completely
            iterators = iterators[1:]

パフォーマンスを向上させるために実際のキューを使用する (David の提案による):

from collections import deque

def alternate(*iterators):
    queue = deque(iterators)
    while len(queue) > 0:
        iterator = queue.popleft()
        try:
            yield next(iterator)
            queue.append(iterator)
        except StopIteration:
            pass

一部のイテレータが有限で、他のイテレータが無限の場合でも機能します。

from itertools import count

for n in alternate(count(), iter(range(3)), count(100)):
    input(n)

版画:

0
0
100
1
1
101
2
2
102
3
103
4
104
5
105
6
106

また、すべてのイテレータが使い果たされた場合は正しく停止します。

リストのようにイテレータ以外のイテラブルを処理したい場合は、次を使用できます

def alternate(*iterables):
    queue = deque(map(iter, iterables))
    ...
于 2016-11-09T00:10:16.300 に答える
1

izip とチェーンを一緒に使用します。

>>> list(itertools.chain.from_iterable(itertools.izip(items, c))) # 2.6 only
['foo', 1, 'bar', 2]

>>> list(itertools.chain(*itertools.izip(items, c)))
['foo', 1, 'bar', 2]
于 2008-12-06T00:08:15.180 に答える
0

簡潔な方法は、itertools.cycle() でジェネレータ式を使用することです。タプルの長い chain() の作成を回避します。

generator = (it.next() for it in itertools.cycle([i1, i2]))
于 2008-12-26T23:04:50.880 に答える
0

itertools.izip()他のいくつかの回答の代わりに を使用zip()すると、パフォーマンスが向上します。

「pydoc itertools.izip」が示すように:

zip() 関数のように機能しますが、リストの代わりにイテレータを返すことでメモリの消費を抑えます。

イテレータの 1 つが無限であっても、Itertools.izip は適切に機能します。

于 2008-12-05T22:46:37.250 に答える
0

なぜ itertools が必要なのですか?

def imerge(a,b):
    for i,j in zip(a,b):
        yield i
        yield j

この場合、a または b の少なくとも 1 つは有限長でなければなりません。これは、zip がイテレータではなくリストを返すためです。出力として反復子が必要な場合は、Claudu ソリューションを使用できます。

于 2008-10-28T21:34:43.410 に答える