15

Python C API 関数を使用PyEval_EvalCodeすると、コンパイルされた Python コードを実行できます。Python コードのブロックをfunction のスコープ内で実行しているかのように実行して、グローバル状態に影響を与えないローカル変数の独自の辞書を作成したいと考えています。

グローバル ディクショナリとローカル ディクショナリを提供できるため、これは簡単に実行PyEval_EvalCodeできます。

PyObject* PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)

私が遭遇した問題は、Python が変数名を検索する方法に関係しています。で実行する次のコードを検討してくださいPyEval_EvalCode

myvar = 300
def func():
    return myvar

func()

myvarPython は内から変数を見つけることができないため、この単純なコードは実際にはエラーを発生させますfunc。外側のスコープのローカル ディクショナリにあってもmyvar、Python はそれを内側のスコープのローカル ディクショナリにコピーしません。この理由は次のとおりです。

Python が変数名を検索するときはいつでも、最初に をチェックlocalsし、次に をチェックglobalsし、最後に をチェックしますbuiltinsモジュール スコープでは、localsglobalsは同じディクショナリ オブジェクトです。したがって、x = 5モジュールスコープのステートメントxは、locals辞書でもあるglobals辞書に配置されます。現在、ルックアップが必要なモジュール スコープで定義された関数は、 function-scope 内でxは見つかりません。これは、Python がモジュール スコープのローカルを関数スコープのローカルにコピーしないためです。しかし、これは通常は問題にはなりません。xlocalsxglobals

x = 5
def foo():
   print(x) # This works because 'x' in globals() == True

Python が外側スコープのローカルを内側スコープのローカルにコピーしているように見えるのは、ネストされた関数のみです。(内部スコープ内で必要な場合にのみ、遅延して行うようにも見えます。)

def foo():
   x = 5
   def bar():
      print(x) # Now 'x' in locals() == True
   bar()


したがって、これらすべての結果として、モジュール スコープでコードを実行するときは、グローバル ディクショナリとローカル ディクショナリが同じオブジェクトであることを確認する必要があります。そうしないと、モジュール スコープ関数がモジュール スコープ変数にアクセスできなくなります。

しかし、私の場合、グローバル辞書とローカル辞書を同じにしたくありません。そのため、関数スコープでコードを実行していることを Python インタープリターに伝える何らかの方法が必要です。これを行う方法はありますか?PyCompileFlagsと追加の引数を調べましたが、PyEval_EvalCodeExこれを行う方法が見つかりません。

4

1 に答える 1

5

Python は実際には、外側のスコープのローカルを内側のスコープのローカルにコピーしません。状態のドキュメントlocals:

locals() が関数ブロックで呼び出された場合、自由変数が返されますが、クラス ブロックでは返されません。

ここで、「フリー」変数とは、ネストされた関数によって閉じられた変数を指します。それは重要な違いです。

あなたの状況の最も簡単な修正は、同じdict オブジェクトをglobalsandとして渡すことlocalsです:

code = """
myvar = 300
def func():
    return myvar

func()
"""
d = {}
eval(compile(code, "<str>", "exec"), d, d)

それ以外の場合は、コードを関数でラップし、コンパイルされたオブジェクトから抽出できます。

s = 'def outer():\n    ' + '\n    '.join(code.strip().split('\n'))
exec(compile(s, '<str>', 'exec').co_consts[0], {}, {})
于 2012-09-04T15:37:50.530 に答える