web-dev-qa-db-ja.com

圧縮されたPython 2番目のジェネレータが短いジェネレータ:サイレントに消費される要素を取得する方法

(潜在的に)異なる長さの2つのジェネレータをZipで解析したい:

for el1, el2 in Zip(gen1, gen2):
    print(el1, el2)

ただし、gen2の要素が少ない場合、gen1の1つの追加要素が「消費」されます。

例えば、

def my_gen(n:int):
    for i in range(n):
        yield i

gen1 = my_gen(10)
gen2 = my_gen(8)

list(Zip(gen1, gen2))  # Last Tuple is (7, 7)
print(next(gen1))  # printed value is "9" => 8 is missing

gen1 = my_gen(8)
gen2 = my_gen(10)

list(Zip(gen1, gen2))  # Last Tuple is (7, 7)
print(next(gen2))  # printed value is "8" => OK

どうやら、値が欠落している(前の例では8)のは、gen1に要素がないことを認識する前に8が読み取られる(つまり、値gen2が生成される)ためです。しかし、この値は宇宙で消えます。 gen2が「長い」場合、そのような「問題」はありません。

[〜#〜] question [〜#〜]:この欠損値を取得する方法はありますか(つまり、前の例では8) )? ...理想的には、可変数の引数を使用します(Zipのように)。

[〜#〜]ノート[〜#〜]I現在、itertools.Zip_longestを使用して別の方法で実装していますが、Zipまたは同等のものを使用して、この欠落している値を取得する方法を本当に思います。

注2私はさまざまな実装のいくつかのテストを作成しましたthis REPL送信して新しい実装を試したい場合:) https://repl.it/@jfthuong/MadPhysicistChester

54

コードを再利用する場合、最も簡単な解決策は次のとおりです。

from more_itertools import peekable

a = peekable(a)
b = peekable(b)

while True:
    try:
        a.peek()
        b.peek()
    except StopIteration:
        break
    x = next(a)
    y = next(b)
    print(x, y)


print(list(a), list(b))  # Misses nothing.

このコードは、セットアップを使用してテストできます。

def my_gen(n: int):
    yield from range(n)

a = my_gen(10)
b = my_gen(8)

印刷されます:

0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
[8, 9] []
3
Neil G

基本的なforループでドロップされた値を取得できないと思います。使い果たされたイテレータはZip(..., ...).__iter__から取得され、一度使い果たされてアクセスできなくなるためです。

Zipを変更すると、ドロップしたアイテムの位置をハックコードで取得できます)

z = Zip(range(10), range(8))
for _ in iter(z.__next__, None):
    ...
_, (one, other) = z.__reduce__()
_, (i_one,), p_one = one.__reduce__() # p_one == current pos, 1 based
import itertools
val = next(itertools.islice(iter(i_one), p_one - 1, p_one))