22

質問はこの投稿の最後にあります。

最初のスニペット:空のローカル変数ディクショナリ。

def outer():
    x = 1
    def inner():
        print "Local variables: %s" % locals()
    return inner()
print outer()

出力: ローカル変数: {}

2 番目のスニペット: inner() 関数内に出力し、ローカル変数エントリを作成します。

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
    return inner()
print outer()

出力:

1
Local variables: {'x': 1}

3 番目のスニペット: 内部関数内からの del x:

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
        del x
    return inner()
print outer()

出力:

>>> outer()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in outer
  File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'x' referenced before assignment
>>>

質問:

  1. 2 番目のスニペットでは、print ステートメントでローカル変数を作成する方法を示しています。
  2. 内部関数内にローカル変数を作成すると、なぜそれを削除できないのですか。

誰かがこれを理解するのを手伝ってくれませんか。

4

3 に答える 3

23

Python では、特に指定しない限り (globalステートメントで、またはnonlocal3.0 以降のステートメントで)、関数内のどこかで変数localsを変更 (変数への割り当て、変数への割り当てdelなど) すると、その変数は有効になります。*

最初のスニペットでは、 を変更xしたり、アクセスしたりすることさえないため、ローカルではありません。実際、それは存在さえしません。簡単だ。

2番目のバージョンはトリッキーです。で変更しないため、 はxに対してローカルではありません。そのため、Python はそれを探し、その変数を持つスコープが見つかるまで、スコープごとに外側に移動します。そして、それは のローカル変数として見つかります。これは、 のクロージャ変数または自由変数であることを意味します。関数にはローカル変数だけでなくクロージャー変数も含まれているため、それがわかります。innerinnerouterinnerlocals

3 番目のバージョンは、 を実行del xすることで .**xにローカルになり、inner.** に表示されlocalsます。ただし、print何も割り当てずに試してみるので、まだ値はありません。だからあなたは得るUnboundLocalError

一般に、Python がここで達成しようとしている基本的な考え方を理解すれば、通常、使用している変数の種類が明らかになります。ただし、不明な点がある場合は、詳細な規則がNaming and Bindingで定義されています。


クロージャーが内部でどのように機能するかを理解したい場合は、関数オブジェクトを調べることから始めることができます。これを試して:

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
    return inner
inner = outer()
print inner.func_closure
print inner.func_code.co_freevars
print outer.func_code.co_cellvars

モジュールのドキュメントには、 、、およびその他の「隠れた」オブジェクトinspectの重要なメンバーがすべてリストされています。functioncode

モジュールを使用してとdisのバイトコードを調べることも役立つ場合があります。*** たとえば、このコードを実行すると、ローカル、セル、およびグローバルのa が表示されます。outerinnerLOAD_FASTLOAD_DEREFLOAD_GLOBAL

しかし、これらすべてが実際にどのように機能するかを本当に理解したい場合は、 Eli Bendersky のブログ「Python internals」にあるシンボル テーブルに関する一連の記事が、ほぼすべてをうまくカバーしています。(それを見つけてコメントで指摘してくれた Ashwini Chaudhary に感謝します。)


* これは、実行時ではなくコンパイル時にチェックされるため、たとえば、混同しようとすると、execPython と自分自身の両方がうまく混乱する可能性があります。

**del変更とアクセスの両方としてカウントされることに注意してください。これは驚くべきことですが、がローカルにdef foo(): del xなるUnboundLocalErrorため が発生し、まったく同じことが値を見つけられないことがわかります。delxdel

*** … CPython 自体 (もちろん) や PyPy など、CPython スタイルのバイトコードを使用する Python 実装を使用していると仮定します。

于 2014-01-13T12:18:39.513 に答える
8

Python は、コンパイル時に変数がどのように使用されるかを調べることにより、ネストされたスコープをサポートします。関数内で割り当てる (または関数内で にバインドするimport) 変数はローカルと見なされ、それ以外はすべて非ローカルです。変数を削除しようとすると、ローカルとしてマークされます。

非ローカル名は親スコープで検索され、見つからない場合はグローバルと見なされます。

2 番目の例でxは、親スコープの名前を参照します。割り当てていないため、ネストされた名前であり、ローカル名前空間で確認できます。これは実際にはローカル名ではなく、自由変数です。その値は、代わりに親スコープから取得されます。

最後の例では、を削除xしてローカル名にしようとしています。何かが割り当てられる前に参照しようとすると、例外が発生します。

これはすべて、Python リファレンスの実行モデルのドキュメントに記載されています。具体的には:

名前がコード ブロックで使用されると、最も近い外側のスコープを使用して解決されます。コード ブロックに表示されるそのようなすべてのスコープのセットは、ブロックの環境と呼ばれます。

名前がブロックにバインドされている場合、それはそのブロックのローカル変数です。名前がモジュール レベルでバインドされている場合、それはグローバル変数です。(モジュール コード ブロックの変数は、ローカルおよびグローバルです。) 変数がコード ブロックで使用されているが、そこで定義されていない場合、それは自由変数です

次の構成体は名前をバインドします: 関数、ステートメント、クラスおよび関数定義への仮パラメーター import(これらは、定義ブロックでクラスまたは関数名をバインドします)、および代入で発生する場合は識別子であるターゲット、forループ ヘッダーの 2 番目の位置with ステートメントのexcept句ヘッダーまたは後。asフォームのimportステートメントはfrom ... import *、アンダースコアで始まるものを除いて、インポートされたモジュールで定義されたすべての名前をバインドします。このフォームは、モジュール レベルでのみ使用できます。

ステートメントで発生するターゲットdelも、この目的のためにバインドされていると見なされます (ただし、実際のセマンティクスは名前のバインドを解除することです)。囲んでいるスコープによって参照されている名前をアンバインドすることは違法です。コンパイラはSyntaxError.

于 2014-01-13T12:19:23.830 に答える
0

説明があります 変数に値があるのに UnboundLocalError が発生するのはなぜですか? abarnert と Martijn による上記の回答と同様に、python docs faq ページで。

于 2014-01-14T02:44:15.373 に答える