web-dev-qa-db-ja.com

Python 3の実行文字列で定義された関数を使用する

次のpython3コードでエラーが発生するのはなぜですか?

a='''
def x():
  print(42)
'''

class Test:
    def __init__(self):
        exec(a)
        x()

t = Test()

このメッセージの結果:

Traceback (most recent call last):
  File "bug.py", line 11, in <module>
    t = Test()
  File "bug.py", line 9, in __init__
    x()
NameError: global name 'x' is not defined
20
Gregwar

注:execはPython 2.xの単純なステートメントでしたが、Python 3.xの関数です。

Python 2.7

aを実行して行われた変更を確認しましょう。

_class Test:
    def __init__(self):
        l, g = locals().copy(), globals().copy()
        exec a           # NOT a function call but a statement
        print locals() == l, globals() == g
        x()

t = Test()
_

出力

_False True
42
_

これは、locals辞書の何かが変更されたことを意味します。 execの前後にlocals().keys()を出力すると、xの後にexecが表示されます。 exex のドキュメントによると、

いずれの場合も、オプション部分を省略した場合、コードは現在のスコープで実行されます。

したがって、ドキュメントに記載されているとおりに動作します。

Python 3.x:

Python 3.xで同じことを実行すると、エラーが発生することを除いて、同様の結果が得られます。

_class Test:
    def __init__(self):
        l, g = locals().copy(), globals().copy()
        exec(a)          # Function call, NOT a statement
        print(locals() == l, globals() == g)
        x()
_

出力

_False True
NameError: name 'x' is not defined
_

exec関数のドキュメント でさえ、

いずれの場合も、オプション部分を省略した場合、コードは現在のスコープで実行されます。

ただし、下部にメモも含まれています。

注:デフォルトのローカルは、以下の関数locals()で説明されているように機能します:デフォルトへの変更ローカル辞書は試行しないでください。関数exec()が戻った後、ローカルに対するコードの影響を確認する必要がある場合は、明示的なローカル辞書を渡します。

したがって、不思議なことに locals()ドキュメント をチェックして見つけます

注:この辞書の内容は変更しないでください。変更は、インタプリタが使用するローカル変数と自由変数の値に影響を与えない場合があります。

したがって、インタプリタはlocals()オブジェクトに加えられた変更を尊重しません。これが、ローカルスコープで定義されているxを認識しない理由です。

しかし、私たちがするとき

_def __init__(self):
    exec(a, globals())
    x()
_

globalsディクショナリに追加するため、機能します。 Pythonは、最初にローカルスコープで、次にクラススコープで、次にグローバルスコープでxを見つけようとし、そこで見つけます。したがって、問題なく実行されます。

15
thefourtheye

Python2.7ではコードが正常に機能しているため、Python3.xを使用していると想定しています。したがって、Python3.xの場合は、行を変更します

exec(a)

exec(a, globals())

xをグローバル名前空間に追加するため。

ドキュメント

9
timgeb

Python3 exec は、マッピングタイプのglobalsおよびlocalsオプションの引数も取ります。これらは、指定されたコード実行のコンテキストとして機能します。

_exec(object[, globals[, locals]])
_

デフォルトでは、ローカルスコープは両方に渡されます。実行されたコードはそれを使用でき、dictを変更することもできますが、実際のローカルスコープには影響しません。 locals() および例を参照してください。

_a = '''
print(t)

def x():
    print(42)
'''

class Test:
    def __init__(self):
        t = 'locals are accessible'
        # same as calling exec(a, locals())
        exec(a)
        print(locals())
        x()

t = Test()
_

出力:

_locals are accessible
{'x': <function x at 0x6ffffd09af0>,
'self': <__main__.Test object at 0x6ffffce3f90>,
't': 'locals are accessible'}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 13, in __init__
NameError: global name 'x' is not defined
_

x呼び出しの後にexecを使用できるようにする場合は、グローバルスコープまたはカスタムスコープを渡す必要があります。

_# global scope
class Test:
    def __init__(self):
        exec(a, globals())
        x()

# custom scope
class Test:
    def __init__(self):
        scope = {}
        exec(a, scope)
        scope['x']()
_
7
famousgarkin