Python UnboundLocalErrorとNameErrorエラーの根源解析

13803 ワード

コードスタイルが相対的にpythonicでない場合は、このようなエラーに遭遇することは少ないかもしれません.もちろんpython言語を使うテクニックを奨励しないわけではありません.このようなタイプのエラーに遭遇した場合、pythonの変数参照関連部分に対する不適切な認識と理解があることを示します.これはpythonに関する概念を理解するのに重要です.これも本稿を書く理由です.
本稿では、閉包に関する概念を理解するための下敷きとして、閉包に関するブログを詳しく整理しますので、ご注目ください.
1.ケーススタディ
クローズド・パッケージに関する概念を整理する過程で、UnboundLocalErrorとNameErrorの2つのエラーがよく発見され、最初は困惑していたかもしれませんが、このようなエラーには手がつけられませんでした.
1.1ケース1:
1 def outer_func():
2     loc_var = "local variable"
3     def inner_func():
4         loc_var += " in inner func"
5         return loc_var
6     return inner_func
7 
8 clo_func = outer_func()
9 clo_func()

エラーメッセージ:
Traceback (most recent call last):
  File "G:\Project Files\Python Test\Main.py", line 238, in 
    clo_func()
  File "G:\Project Files\Python Test\Main.py", line 233, in inner_func
    loc_var += " in inner func"
UnboundLocalError: local variable 'loc_var' referenced before assignment

1.2ケース2:
1 def get_select_desc(name, flag, is_format = True):
2     if flag:
3         sel_res = 'Do select name = %s' % name
4     return sel_res if is_format else name
5 
6 get_select_desc('Error', False, True)

エラーメッセージ:
Traceback (most recent call last):
  File "G:\Project Files\Python Test\Main.py", line 247, in 
    get_select_desc('Error', False, True)
  File "G:\Project Files\Python Test\Main.py", line 245, in get_select_desc
    return sel_res if is_format else name
UnboundLocalError: local variable 'sel_res' referenced before assignment

1.3ケース3:
 1 def outer_func(out_flag):
 2     if out_flag:
 3         loc_var1 = 'local variable with flag'
 4     else:
 5         loc_var2 = 'local variable without flag'
 6     def inner_func(in_flag):
 7         return loc_var1 if in_flag else loc_var2
 8     return inner_func
 9 
10 clo_func = outer_func(True)
11 print clo_func(False)

エラーメッセージ:
Traceback (most recent call last):
  File "G:\Project Files\Python Test\Main.py", line 260, in 
    print clo_func(False)
  File "G:\Project Files\Python Test\Main.py", line 256, in inner_func
    return loc_var1 if in_flag else loc_var2
NameError: free variable 'loc_var2' referenced before assignment in enclosing scope

上の3つの例は少しでたらめに見えるかもしれませんが、実際にはエラーのようなコードは、上記の例では影を見つけることができます.ここでは,関連概念を説明するためだけに,例自体の合理性にあまり注目する必要はない.
2.エラーの原因
pythonには変数、関数、またはクラスの宣言概念がないためです.CやC++の習慣でpythonを書くと、間違いの根源がどこにあるか発見しにくいかもしれません.
まず、このような誤った公式解釈を見てみましょう.
When a name is not found at all, a NameError exception is raised. If the name refers to a local variable that has not been bound, a UnboundLocalError exception is raised. UnboundLocalError is a subclass of NameError .
意味は次のとおりです.
変数が参照されていますが、変数名が見つかりません.このタイプのエラーはNameErrorです.名前がまだバインドされていないローカル変数名の場合、このタイプのエラーはNameErrorのUnboundLocalError です.
次のようなNameErrorタイプのエラーは理解できるかもしれません.
1 my_function()
2 def my_function():
3     pass

python解釈器がdef my_に実行されるとしたらfunction()の場合my_にバインドされます.function,my_functionは、メモリ内の関数実行のエントリも表します.だからその前にmy_を使うfunctionではNameErrorエラーが発生します.
では、上記の例では変数を使用する前に、付与操作(バインド操作と見なすことができますが、後で説明します)がありますが、なぜ参照時にエラーが発生しますか?可視性の定義も可能
割り当て操作が実行されていないためといえば、変数名がローカルネーミングスペースで表示されるのはなぜですか?(見えない場合は、NameError:global name'xxx'is not defined、UnboundLocalError定義により可視性)も判断できる
問題はいったいどこにあるのか.上記の3つの例の誤りをどのように正しく理解しますか?
3.可視性とバインド
簡単に言えば、ここではネーミングスペースと変数検索ルールLGBに関する概念を紹介しません.
CまたはC++では、変数または関数を宣言して定義すれば、直接使用できます.しかし、Pythonで名前を参照するには、名前が表示され、バインドされている必要があります.
まずいくつかの概念を理解します.
  • code block:ユニット(Unit)として実行されるpythonプログラムテキスト.たとえば、モジュール、関数体、クラスの定義などです.
  • scope:code blockでnameの可視性を定義します.
  • block’s environment:1つのcode blockについて、そのすべてのscopeに見られるnameの集合がblockの環境を構成する.
  • bind name:次の操作はバインド操作とみなされます.
  • 関数のパラメータ
  • import宣言
  • クラスと関数の定義
  • 付与動作
  • forループヘッダ
  • 異常取得に関連する付与変数
  • local variable:nameがblockにバインドされている場合、変数はblockのlocal variableです.
  • global variable:nameがmoduleにバインドされている場合、この変数はglobal variableと呼ばれます.
  • free variable:1つのnameが1つのblockで参照されているが、コードブロックで定義されていない場合、この変数をfree variableと呼ぶ.

  • Free variableは比較的重要な概念であり、閉パッケージで参照される親関数のローカル変数はfree variableであり、このfree variableはcellオブジェクトに格納される.これはクローズドパッケージ関連の記事で紹介します. 
    scopeは関数に拡張性がありますが、クラス定義には拡張性がありません.
    分析して整理する:
    上記のいくつかの概念の紹介を経て、1つの変数がそのcode blockにバインド操作がある限り、code blockのscopeにはこの変数が含まれていることが分かった.
    すなわち、バインド操作が決定され、バインドされたnameは、nameが真のバインド操作を行う前に、現在のscope(関数であれば定義されたscopeも含む)で表示される.
    ここでは、name操作をバインドする前にnameを参照すると、nameが表示されていても問題が発生するという問題があります.
    If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block. This can lead to errors when a name is used within a block before it is bound. This rule is subtle. Python lacks declarations and allows name binding operations to occur anywhere within a code block. The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.
    上記の公式に説明された最初の文と最後の文に注意してください.
    総じて、1つのcode blockにおいて、すべてのバインド操作でバインドされたnameはlocal variableと見なすことができる.しかし、このnameは、バインド操作が実行されるまで本当に参照できません.
    これらの概念があれば、上の3つのケースを一つ一つ分析してみましょう.
    4.エラー解析
    4.1ケース一分析
    outer_でfuncでは変数loc_を定義しましたvar、付与はバインド操作であるためloc_varは可視性を有し、特定の文字列オブジェクトにバインドされている.
    しかし、定義された関数inner_funcでは参照できませんが、関数のscopeはその中で定義されているすべてのscopeに拡張できるのではないでしょうか.
    次に、公式の2つの文字の説明を見てみましょう.
    When a name is used in a code block, it is resolved using the nearest enclosing scope.
    この言葉は、nameが参照されると、最近のscopeで参照されるnameの定義を探していることを示しています.明らかにloc_var+=「in inner func」という文のloc_varはまず内部関数inner_funcでname locを探すvar.
    この文は実際にloc_に等しいvar = loc_var+「in inner func」、等号右側のloc_var変数は最初に使用されますが、ここではouter_は使用されません.funcで定義されたloc_var、関数inner_funcのscopeにloc_がありますvarの付与操作なので、この変数はinner_funcのscopeでinnerとしてfuncのlocal variableの1つが表示されます.
    しかし、loc_を本当にバインドするには、文の実行が完了するまで待たなければなりません.var.つまり、この文ではinner_を使用しています.func blockでバインドされる前のlocal variable.上記のエラータイプの定義によれば、UnboundLocalError.です.
    4.2ケース2分析
    この例では、問題があるように見えますが、どのように説明するか分かりません.
    参照はバインド操作の後に発生し、変数は正常に参照できるはずです.しかし、問題は、付与文(バインド操作)が必ずしも実行されないことです.バインド操作がない場合、変数の参照に問題があるに違いありません.これは前に説明しました.
    しかし、もう一つの疑問は、付与文が実行されていない場合、変数が現在のblockでなぜ表示されるのかということです.
    この問題については、The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.
    バインド操作がある限り(実際に実行されているかどうかにかかわらず)、バインドされたnameはlocal variableとして、すなわち現在のblockで表示され得る.scanning textは、コードが実行される前に発生する.
    4.2ケース3分析
    この例は主にfree variableへの参照の問題を説明する.同時にこの例はfree variableの使用も示している.
    閉パッケージの作成inner_func時、loc_var 1とloc_var 2を親関数としてouter_funcの2つのlocal variableはその内部にinner_funcのscopeでは表示されます.閉パケットを返した後、閉パケットでouter_が参照されます.funcのlocal variableをfree variableと呼ぶ.
    閉パケット内のfree variableが参照できるかどうかは、特定のオブジェクトにバインドされているかどうかによって異なります.
    5.導入事例
    次に例を示します.
    1 import sys
    2 
    3 def add_path(new_path):
    4     path_list = sys.path
    5 
    6     if new_path not in path_list:
    7         import sys
    8         sys.path.append(new_path)
    9 add_path('./')

    普段何気なくこのような間違いを犯す可能性があります.これも典型的なUnboundLocalErrorの間違いです.上の分析過程をよく読んで理解すれば、この間違いを理解できる原因を与えるべきだと信じています.まだクリアしていない場合は、もう一度お読みください:-)
    何か質問があれば討論を歓迎します.
    転載先:https://www.cnblogs.com/yssjun/p/9873689.html