1. ホーム
  2. python

yield from" 文を Python 2.7 のコードに変換する

2023-10-06 07:05:16

質問

私はPython 3.2で以下のコードを持っていた、私はPython 2.7でそれを実行したいと思った。私はそれを変換しました。 missing_elements のコードを両方のバージョンに入れました)しかし、私はそれを行うための最も効率的な方法であるかどうかわからない。基本的に、もし2つの yield from の中で上半分と下半分で以下のような呼び出しがあった場合はどうなるでしょうか? missing_element 関数で呼び出すのですか?上半分と下半分のエントリーは、1つのリストで互いに追加され、親再帰関数が yield from を持つ親再帰関数が両方の半分を一緒に呼び出して使用できるようにするためですか?

def missing_elements(L, start, end):  # Python 3.2
    if end - start <= 1: 
        if L[end] - L[start] > 1:
            yield from range(L[start] + 1, L[end])
        return

index = start + (end - start) // 2

# is the lower half consecutive?
consecutive_low =  L[index] == L[start] + (index - start)
if not consecutive_low:
    yield from missing_elements(L, start, index)

# is the upper part consecutive?
consecutive_high =  L[index] == L[end] - (end - index)
if not consecutive_high:
    yield from missing_elements(L, index, end)

def main():
    L = [10, 11, 13, 14, 15, 16, 17, 18, 20]
    print(list(missing_elements(L, 0, len(L)-1)))
    L = range(10, 21)
    print(list(missing_elements(L, 0, len(L)-1)))

def missing_elements(L, start, end):  # Python 2.7
    return_list = []                
    if end - start <= 1: 
        if L[end] - L[start] > 1:
            return range(L[start] + 1, L[end])

    index = start + (end - start) // 2

    # is the lower half consecutive?
    consecutive_low =  L[index] == L[start] + (index - start)
    if not consecutive_low:
        return_list.append(missing_elements(L, start, index))

    # is the upper part consecutive?
    consecutive_high =  L[index] == L[end] - (end - index)
    if not consecutive_high:
        return_list.append(missing_elements(L, index, end))
    return return_list

どのように解決するのですか?

歩留まりの結果を利用しない場合*は、次のようにします。 常に これを回す。

yield from foo

... をこのようにします。

for bar in foo:
    yield bar

パフォーマンスコスト**はあるかもしれませんが、セマンティックな違いは決してありません。


2 つのハーフ (上側と下側) のエントリは 1 つのリストで互いに追加され、yield from を持つ親再帰関数が両方のハーフを一緒に呼び出して使用できるようになりますか。

いいえ!イテレータとジェネレータの全体的なポイントは、実際のリストを構築してそれらを一緒に追加しないことです。

しかし の効果 も同様で、1つから降伏し、次に別のものから降伏するだけです。

上半分と下半分を "lazy list" と考えると、そう、これはより大きな "lazy list" を作成する "lazy append" として考えることができます。そして、もしあなたが list を呼び出すと、もちろん、親関数の結果に対して を得ることができます。 list を実行した場合に得られるはずだった二つのリストを足し合わせたものと同じです。 yield list(…) ではなく yield from … .

でも、逆に考えた方がわかりやすいと思います。これは、まさに for ループが行うことと全く同じです。

2つのイテレータを変数に保存し、ループさせた場合 itertools.chain(upper, lower) をループさせたとしたら、それは1つ目をループさせてから2つ目をループさせたのと同じですよね?ここでは違いはありません。実際には chain をそのまま実装することができます。

for arg in *args:
    yield from arg


* ジェネレータが呼び出し元に渡す値ではなく、ジェネレータ内のyield式の値そのものです(呼び出し元は send メソッドを使って呼び出し元から来るものです)、で説明されているように PEP 342 . あなたの例ではこれらを使っていませんね。そして、実際のコードでも使っていないことに賭けてもいいくらいです。しかし、コルーチン形式のコードではしばしば yield from 式の値を使うことがあります。 PEP 3156 を参照してください。このようなコードは通常 Python 3.3 ジェネレータの他の機能、特に、新しい StopIteration.value から、同じ PEP 380 を導入した yield from -を導入した PEP 380 で、書き直さなければならないでしょう。しかし、そうでない場合は、PEPを使えば、完全に恐ろしいほど面倒な同等のものを表示してくれますし、もちろん、気にしない部分を切り詰めることができます。そして、もし式の値を使わなければ、上の2行に縮小されます。

** 巨大なものではありませんし、Python 3.3を使うか、コードを完全に再構築しない限り、どうすることもできません。リスト内包を Python 1.5 のループに変換するのとまったく同じケース、または、バージョン X.Y で新しい最適化があり、古いバージョンを使用する必要がある他のケースと同じです。