web-dev-qa-db-ja.com

なぜexecがサブ関数を持つ関数で機能しないのですか?

サブ関数を持つ関数でexecを使用できないようです...

なぜ誰もこれがPythonコードが機能しないのを知っていますか?test2のexecでエラーが発生します。適切な理由のために私はそれ以外の場合は使用しません。

#!/usr/bin/env python
#

def test1():
    exec('print "hi from test1"')

test1()

def test2():
    """Test with a subfunction."""
    exec('print "hi from test2"')
    def subfunction():
        return True

test2()

編集:私はバグをサブ関数に関数を持つように絞り込みました。 raiseキーワードとは関係ありません。

53
anon

正しい。コンテキストを指定しない限り、サブ関数を持つ関数でexecを使用することはできません。ドキュメントから:

Execが関数で使用され、関数に自由変数を含むネストされたブロックが含まれる場合、execは、execのローカル名前空間を明示的に指定しない限り、SyntaxErrorを発生させます。 (つまり、「exec obj」は違法ですが、「exec obj in ns」は有効です。)

これには十分な理由があり、おそらく日曜日の夜でなければ理解できるでしょう。さて、次の質問:なぜexecを使用しているのですか?必要になることはほとんどありません。正当な理由があると言います。私はそれについて懐疑的です。 ;)正当な理由がある場合は、回避策を説明します。 :-P

まあ、とにかくここにあります:

def test2():
    """Test with a subfunction."""
    exec 'print "hi from test2"' in globals(), locals()
    def subfunction():
        return True
65
Lennart Regebro

Pythonでは、ローカル変数が辞書locals()に格納されているように見えますが、通常は格納されていません。代わりに、ほとんどがスタックに格納され、これにより、毎回辞書検索を行う必要がある場合よりもローカル変数の検索が高速になりますlocals()関数を使用すると、すべてのローカル変数から作成された新しい辞書が得られます。 locals()への割り当ては通常機能しません。

このシナリオにはいくつかの例外があります。

関数内で修飾されていないexecを使用すると、Pythonは最適化を無効にし、ローカル変数に実際の辞書を使用します。つまり、exec内から変数を作成または更新できますが、その関数内のすべてのローカル変数アクセスはより遅く実行されます。

他の例外は、関数をネストすると、内部関数が外部関数スコープ内のローカル変数にアクセスできることです。これを行うと、変数はスタックに保存されるのではなく、「セル」オブジェクトに保存されます。インダイレクションの追加レベルにより、スコープ付き変数の使用は、内部関数または外部関数のどちらからアクセスする場合でも遅くなります。

遭遇した問題は、ローカル変数の通常の格納方法に対するこれら2つの例外には互換性がないことです。辞書に変数を保存し、同時にセル参照を介してアクセスすることはできません。 Python 2.xは、スコープ変数を使用しようとしていないこのような場合でも、execを許可しないことでこれを修正します。

28
Duncan

これはかなり興味深いケースです。

_>>> def func():
...     exec('print "hi from func"')
...     def subfunction():
...         return True
... 
  File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'func' because 
it contains a nested function with free variables
_

これが実際に機能しない理由は、subfunctionに自由変数が含まれており、Python 2、execで理論的にローカルのローカル変数を変更できるためです。スコープを含む場合、変数をグローバルスコープまたは親関数スコープからバインドするかどうかを決定することはできません。PythonのZenの節の1つは「あいまいさに直面して、推測する誘惑を拒否します。」これはPython 2が行うことです。

質問は次のとおりです。この無料(非バインド)変数とは何ですか?さて、それはTrueです!

実際、Noneで再現可能です:

_>>> def func():
...     exec('print "hi from func"')
...     def subfunction():
...         return None
... 
  File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'test2' because it contains a nested
function with free variables
_

Noneに割り当てることができず、バイトコードではconstantと見なされますが、バグのあるパーサーはバインドされていないと見なしますここで変数。

しかし、それを_1_に置き換え、問題なく動作する場合:

_>>> def test2():
...     exec('print "hi from func"')
...     def subfunction():
...         return 1
... 
>>>
_

このエラーを回避するには、execが使用するグローバルおよび場合によってはローカルを明示的に指定します。たとえば、

_>>> def test2():
...     exec 'print "hi from test2"' in {}
...     def subfunction():
...         return None
...
>>>
_

In Python 3、execは単なる関数であり、パーサーまたはバイトコードコンパイラによって特別に処理されません。InPython 3 execは関数ローカル名を再バインドできないため、このSyntaxErrorとあいまいさは存在しません。


Python 2 vs 3の互換性の特殊なケースは、 Python 2.7ドキュメント

exec(expr, globals)という形式は_exec expr in globals_と同等であり、exec(expr, globals, locals)という形式は_exec expr in globals, locals_と同等です。 execのタプル形式は、Python 3との互換性を提供します。ここで、execはステートメントではなく関数です。

ネストされた関数を持つ関数でのexecの処理のバグ(issue 21591) ;があったため、Tupleフォームは常に100%互換ではありませんでした。 Python 2.7.8まで、次のコードは例外をスローした可能性があります。

_def func():
    exec('print "hi from test2"', {})
    def subfunction():
        return None
_

これはPython 2.7.9で修正され、スローされなくなりました。

6
Antti Haapala

これは、Python 3.1.3、print関数を使用するようにprintステートメントを変更した後、うまく機能します。

Python 2.6では、SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables、私はそれがバグだとは思わない。

4
Lie Ryan

エラーは私にはかなり明白なようです:

SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables

詳細については、pep 227を参照してください: http://www.python.org/dev/peps/pep-0227/

1
Wolph

dictおよびlist内包表記は、Python 2.7.5

たとえば、これはPython 2.7.5では失敗しますが、Python 2.7.12:

def func():
    exec('print("a")')
    (str(e) for e in range(10))

で:

  File "./a.py", line 4
    exec('print("a")')
SyntaxError: unqualified exec is not allowed in function 'func' it contains a nested function with free variables

おそらく、内部的にバイトコード内の関数にコンパイルされた可能性があります。

TODOは修正コミットを見つけます。それは私のgit log --grep foo。

dict内包表記に類似:

def func():
    exec('print("a")', {e:str(e) for e in range(10)})

これは、global引数の共通パラメーターであるため、特に悪いです。

また、次の場所で発生します: https://github.com/sphinx-doc/sphinx/issues/5417#issuecomment-421731085