web-dev-qa-db-ja.com

python exec()のグローバルとローカル

Execを使用してpythonコードの一部を実行しようとしています。

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(object):
  a_ref = A
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

次の出力になります

locals: {'A': <class 'A'>}
A: <class 'A'>
Traceback (most recent call last):
  File "python_test.py", line 16, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 8, in <module>
  File "My Code", line 9, in B
NameError: name 'A' is not defined

ただし、コードをこれに変更すると、

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(A):
  pass
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

それからそれはうまくいきます-次の出力を与えます-

locals: {'A': <class 'A'>}
A: <class 'A'>
{'A': <class 'A'>, 'B': <class 'B'>}

明らかにAが存在し、アクセス可能です-最初のコードの何が問題になっていますか?私は2.6.5を使用しています、乾杯、

コリン

*更新1 *

クラス内のlocals()をチェックすると-

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(object):
  print locals()
  a_ref = A
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

次に、locals()が両方の場所で同じではないことが明らかになります-

locals: {'A': <class 'A'>}
A: <class 'A'>
{'__module__': '__builtin__'}
Traceback (most recent call last):
  File "python_test.py", line 16, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 8, in <module>
  File "My Code", line 10, in B
NameError: name 'A' is not defined

しかし、これを行えば問題はありません-

def f():
  class A(object):
    pass

  class B(object):
    a_ref = A

f()

print 'Finished OK'

*更新2 *

わかりましたので、ここのドキュメント- http://docs.python.org/reference/executionmodel.html

'クラス定義は、名前を使用および定義できる実行可能なステートメントです。これらの参照は、名前解決の通常の規則に従います。クラス定義の名前空間は、クラスの属性ディクショナリになります。クラススコープで定義された名前はメソッドでは表示されません。

'A'は、Bの定義である実行可能ステートメント内の自由変数として利用できるようにする必要があるようです。これは、上記のf()を呼び出すと発生しますが、 exec()を使用します。これは、次のようにすると簡単に表示できます-

my_code = """
class A(object):
  pass

print 'locals in body: %s' % locals()
print 'A: %s' % A

def f():
  print 'A in f: %s' % A

f()

class B(object):
  a_ref = A
"""

出力する

locals in body: {'A': <class 'A'>}
A: <class 'A'>
Traceback (most recent call last):
  File "python_test.py", line 20, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 11, in <module>
  File "My Code", line 9, in f
NameError: global name 'A' is not defined

だから私は新しい質問だと思います-なぜそれらのローカルは関数とクラス定義の自由変数として公開されていないのですか-それはかなり標準的なクロージャシナリオのようです。

35
hawkett

まあ、それは実装のバグか、文書化されていない設計上の決定のどちらかだと思います。問題の核心は、モジュールスコープの名前バインディング操作がグローバル変数にバインドする必要があることです。それが達成される方法は、モジュールレベルでは、globals()IS locals()(インタプリタで試してみてください)であるため、名前バインディングを行うと、これは、いつものように、グローバルでもあるlocals()ディクショナリに対して行われるため、グローバル変数が作成されます。

変数を検索するときは、最初に現在のローカルをチェックし、名前が見つからない場合は、変数が見つかるか、モジュールスコープに到達するまで、変数名を含むスコープのローカルを再帰的にチェックします。これに到達したら、モジュールスコープのローカルであるはずのグローバルをチェックします。

>>> exec(compile("import sys\nprint sys._getframe().f_code.co_name", "blah", "exec"), {}, {})
<module>
>>> exec("a = 1\nclass A(object):\n\tprint a\n", {}, {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 2, in <module>
  File "<string>", line 3, in A
NameError: name 'a' is not defined
>>> d = {}
>>> exec("a = 1\nclass A(object):\n\tprint a\n", d,d)
1

この動作が継承が機能した理由です(名前の検索では、実際にAが含まれていたコードオブジェクトのスコープlocals()が使用されました)。

結局のところ、これはCPython実装の醜いハック、特別なケースのグローバルルックアップです。それはまた、いくつかの無意味な人工的な状況を引き起こします-例:

>>> def f():
...     global a
...     a = 1
...
>>> f()
>>> 'a' in locals()
True

これは、python言語リファレンス)のセクション4.1(命名とバインディング)を読んでいるときにインタプリタをいじることに基づいたすべての私の推論であることに注意してください。これは決定的なものではありませんが( CPythonのソース)、私はその振る舞いについて正しいと私はかなり確信しています。

18
Roee Shenberg

print locals()およびglobals()の後に、execが「未定義」例外をスローする理由がわかります。これを試すことができます

d = dict(locals(), **globals())
exec (code, d, d)
9
Zoe
my_code = """
class A(object):
    pass

class B(object):
    a = A
"""

my_code_AST = compile(my_code, "My Code", "exec")
extra_global = "hi"
global_env = {}
exec my_code_AST in global_env
print "global_env.keys() =", global_env.keys()
print "B.a =", global_env["B"].a

プリント

global_env.keys() = ['__builtins__', 'A', 'B']
B.a = <class 'A'>

ホーケット、あなたは言う、

そのようなローカルを使用したい主な理由は、pythonがグローバルに配置する他のすべてのものを使用せずに、コード文字列で定義されたすべてのものを取得することでした。

Execを使用して、グローバルに__builtins__が定義されていない場合、execは1つの項目__builtins__をグローバルに追加するため、A、B、および__builtins__を取得します。 __builtins__自体は大きなディクショナリですが、削除する要素は常に同じです(削除する前に、コードの使用が完了するまで待機する限り)。 exec()の下に文書化 ここ

built in functions の下のevalのドキュメントは言う

グローバルディクショナリが存在し、「builtins」がない場合、式が解析される前に、現在のグローバルがグローバルにコピーされます。

しかし、実際には__builtins__をコピーするだけのようです。

(そして、他の誰もが言ったこと:グローバルとローカルを同じに設定するか、別のlocal_envなしでexec my_code_AST in global_envと言ってください。)

あなたの質問がexecステートメントをファイルスコープのように動作させる方法である場合、リンクされた質問とバグのいくつかのヒントに従い、グローバルとローカルの単一のディクショナリを渡すことで機能しました。どうやらファイルスコープは、ローカル宣言が自動的にグローバルスコープに配置される特殊なケースです。

exec code in dict()
2
Don Kirkby