1

テストコードは次のとおりです。

units = [1, 2]
tens = [10, 20]
nums = (a + b for a in units for b in tens)
units = [3, 4]
tens = [30, 40]
[x for x in nums]

3 行目のジェネレーター式 ( ) がイテレーターを形成するという仮定の下で、最終結果はおよびnums = ...に最終的に割り当てられた値を反映すると予想されます。OTOH、そのジェネレーター式が 3 行目で評価され、結果のタプルが生成される場合、andの最初の定義が使用されることを期待します。unitstensunitstens

私が見ているのはMIXです。つまり、結果は[31, 41, 32, 42]!?

誰でもこの動作を説明できますか?

4

1 に答える 1

3

ジェネレーター式は、ある種の関数を作成します。引数が 1 つだけで、最も外側の iterable です。

unitsこれは であり、ジェネレーター式が作成されるときにジェネレーター式への引数としてバインドされます。

他のすべての名前は、ローカル (aおよび などb)、グローバル、またはクロージャーのいずれかです。tensはグローバルとして検索されるため、ジェネレーターを進めるたびに検索されます。

その結果、units3 行目のジェネレーターにバインドさtensれ、最終行のジェネレーター式を反復処理したときにルックアップされます。

これは、ジェネレーターをバイトコードにコンパイルし、そのバイトコードを検査するときに確認できます。

>>> import dis
>>> genexp_bytecode = compile('(a + b for a in units for b in tens)', '<file>', 'single')
>>> dis.dis(genexp_bytecode)
  1           0 LOAD_CONST               0 (<code object <genexpr> at 0x10f013ae0, file "<file>", line 1>)
              3 LOAD_CONST               1 ('<genexpr>')
              6 MAKE_FUNCTION            0
              9 LOAD_NAME                0 (units)
             12 GET_ITER
             13 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             16 PRINT_EXPR
             17 LOAD_CONST               2 (None)
             20 RETURN_VALUE

バイトコードはMAKE_FUNCTIONジェネレータ式コード オブジェクトを関数に変換し、引数として渡してすぐに呼び出されiter(units)ます。ここtensでは名前はまったく言及されていません。

これは、元のジェネレーター PEPに記載されています。

最も外側の for 式のみがすぐに評価され、他の式はジェネレーターが実行されるまで延期されます。

g = (tgtexp  for var1 in exp1 if exp2 for var2 in exp3 if exp4)

次と同等です。

def __gen(bound_exp):
    for var1 in bound_exp:
        if exp2:
            for var2 in exp3:
                if exp4:
                    yield tgtexp
g = __gen(iter(exp1))
del __gen

およびジェネレーター式の参照では:

ジェネレーター式で使用される変数は、__next__()ジェネレーター オブジェクトに対してメソッドが呼び出されると、(通常のジェネレーターと同じ方法で) 遅延評価されます。ただし、一番左のfor句はすぐに評価されるため、ジェネレータ式を処理するコード内で他の可能性のあるエラーが発生する前に、それによって生成されたエラーを確認できます。後続forの句は、前の for ループに依存する可能性があるため、すぐに評価できません。例: (x*y for x in range(10) for y in bar(x)).

PEP には、 (最も外側の iterable 以外の) 名前が遅くバインドされる理由を説明する優れたセクションがあります。 Early Binding vs. Late Bindingを参照してください。

于 2014-03-27T17:06:43.643 に答える