1

名前が定義されていない場合とバインドされていない場合は、実行時に と がそれぞれ表示されNameErrorます。UnboundLocalErrorしかし、実行時に名前の評価がどのように行われるのかは明確ではありません。私は次のことを想定しています:

コードスニペットの例を検討してください

def foo():
    a=3
    def bar():
        return a
    tmp=bar()
    res=a+tmp
    return res

bar関数が呼び出されると、新しい実行フレームが作成されます。このフレームを としbar_frameます。bar_frame.f_localディクショナリに含まれる要素はありません。ただしbar_frame.f_back.f_locals、4 つの名前と値のペアが含まれています。など

私の理解:名前評価には次のアルゴリズムがあります。

  1. で検索しようとしてnameいますcurrentframe.f_locals

    1.1currentframe.f_localsグローバル名前空間に対応し、適切な名前が見つからない場合はスローするNameError

    1.1 適切な名前が見つかり、境界がある場合は返すcurrentframe.f_locals[name]

    1.2 適切な名前が見つかり、無制限の場合はUnboundLocalNameエラーをスローします。

  2. で検索しようとしてnameいますcurrentframe.f_back.f_locals

私の理解を確認してください。

4

1 に答える 1

2

あなたが説明するアルゴリズムは、動的スコープとして知られる方法で動作します。この場合、非ローカル名は実行時に呼び出し元の環境から取得されます。しかし、Python は動的スコープを使用しません。レキシカルスコープを使用します。これは、非ローカル名が語彙的に周囲の関数から解決されることを意味します。たとえば、この関数から返される関数f:

def f():
    x = 5
    def g():
        print(x)
    return g

... 次のように呼び出された場合でも、常に 5 を出力します。

def h():
    g = f()
    x = 10
    g()

gクロージャーと呼ばれます。どの名前がローカルではないか、それらがどの程度「遠くまで」参照するか (関数内の関数内に関数を含めることができます) は、実行時ではなく、バイトコード コンパイラによって決定されます。バイトコードにはこのすべての情報が含まれているため、実行時に、インタープリターは名前を解決する間、複数のフレームはおろか、何も検索しません。使用されているバイトコード命令の種類から、どこを見ればよいかを常に正確に認識しています。

補遺: フレーム オブジェクトが存在し、すべてのローカルの名前と値を持ち、呼び出し元のフレームを参照する理由は無関係です。ローカルは、デバッグを容易にするために保存されます (および、呼び出し関数のローカルに影響を与える厄介なハックもあります)。f_backデバッグとダーク マジックも存在します。最も顕著なのは、スタック トレースが機能するために必要です。

于 2014-03-01T12:20:58.533 に答える