Pythonグローバル、ローカル、およびUnboundLocalError
-
03-07-2019 - |
質問
最近 UnboundLocalError
のケースに出くわしましたが、奇妙に思えます:
import pprint
def main():
if 'pprint' in globals(): print 'pprint is in globals()'
pprint.pprint('Spam')
from pprint import pprint
pprint('Eggs')
if __name__ == '__main__': main()
生成されるもの:
pprint is in globals()
Traceback (most recent call last):
File "weird.py", line 9, in <module>
if __name__ == '__main__': main()
File "weird.py", line 5, in main
pprint.pprint('Spam')
UnboundLocalError: local variable 'pprint' referenced before assignment
pprint
は globals
に明確にバインドされており、次のステートメントの locals
にバインドされます。誰かが pprint
を globals
のバインディングに解決できない理由の説明をここで提供できますか?
編集:適切な回答のおかげで、関連する用語で質問を明確にできます:
コンパイル時に、識別子 pprint
がフレームに対してローカルとしてマークされます。実行モデルには、ローカル識別子がバインドされているフレーム内のどこの区別はありませんか? 「このバイトコード命令までグローバルバインディングを参照し、その時点でローカルバインディングにリバウンドされている」と言えますか?または実行モデルはこれを考慮しませんか?
解決
Pythonのように見える from pprint import pprint
行を見て、 main()
before <のローカル名として pprint
をマークします/ em>任意のコードを実行します。 Pythonはpprintをローカル変数と見なすので、「割り当て」の前に pprint.pprint()
でそれを参照します。 from..import
ステートメントを使用すると、そのエラーがスローされます。
それは、私ができる限り理にかなっています。
モラルは、もちろん、これらの import
ステートメントを常にスコープの先頭に置くことです。
他のヒント
驚きはどこですか?そのスコープ内で再割り当てしたスコープに対してグローバルな任意変数は、コンパイラによってそのスコープに対してローカルとしてマークされます。
インポートの処理が異なる場合、それは意外な見方です。
ただし、そこで使用されているシンボルに基づいてモジュールに名前を付けない、またはその逆を行う場合があります。
まあ、それは私が少し実験するのに十分面白かったので、 http:/を読みました。 /docs.python.org/reference/executionmodel.html
その後、あちこちでコードをいじくり回しました。これは私が見つけることができるものです:
コード:
import pprint
def two():
from pprint import pprint
print globals()['pprint']
pprint('Eggs')
print globals()['pprint']
def main():
if 'pprint' in globals():
print 'pprint is in globals()'
global pprint
print globals()['pprint']
pprint.pprint('Spam')
from pprint import pprint
print globals()['pprint']
pprint('Eggs')
def three():
print globals()['pprint']
pprint.pprint('Spam')
if __name__ == '__main__':
two()
print('\n')
three()
print('\n')
main()
出力:
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Eggs'
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
pprint is in globals()
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
<function pprint at 0xb7d596f4>
'Eggs'
two()
pprint import pprint
のメソッドでは、 globals
の名前 pprint
をオーバーライドしません、 global
キーワードは two()
の範囲では使用されないためです。
メソッド three()
では、ローカルスコープに pprint
名の宣言がないため、デフォルトのグローバル名 pprint
になります。モジュール
main()
では、最初にキーワード global
が使用されるので、 pprint
へのすべての参照はメソッド main()
のスコープは、 global
の名前 pprint
を参照します。ご覧のとおり、最初はモジュールであり、 from pprint import pprint
<を実行するときのメソッドで global
namespace
でオーバーライドされます
これは質問自体に答えているわけではないかもしれませんが、それでも私が思うに興味深い事実がいくつかあります。
=====================
編集別の興味深いこと。
モジュールがある場合:
mod1
from datetime import datetime
def foo():
print "bar"
および別の方法:
mod2
import datetime
from mod1 import *
if __name__ == '__main__':
print datetime.datetime.now()
モジュール datetime
を mod2
にインポートしたため、一見正しいと思われます。
mod2をスクリプトとして実行しようとすると、エラーがスローされます:
Traceback (most recent call last):
File "mod2.py", line 5, in <module>
print datetime.datetime.now()
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
mod2 import *からの2番目のインポート *
がネームスペースの名前 datetime
をオーバーライドしたため、最初の import datetime
はもう有効ではありません。
モラル:したがって、インポートの順序、インポートの性質(x import *から)、およびインポートされたモジュール内のインポートの認識-事項。
この質問は数週間前に回答されましたが、回答を少し明確にできると思います。最初にいくつかの事実。
1:Pythonでは、
import foo
とほぼ同じ
foo = __import__("foo", globals(), locals(), [], -1)
2:関数でコードを実行するときに、Pythonが関数でまだ定義されていない変数を検出すると、グローバルスコープを検索します。
3:Pythonには、「ローカル」と呼ばれる関数に使用する最適化があります。 Pythonが関数をトークン化するとき、割り当てられたすべての変数を追跡します。これらの各変数に、単調に増加するローカル整数からの番号を割り当てます。 Pythonは関数を実行するときに、ローカル変数と同じ数のスロットを持つ配列を作成し、「まだ割り当てられていない」ことを意味する特別な値を各スロットに割り当てます。 。まだ割り当てられていないローカルを参照すると、Pythonはその特別な値を認識し、UnboundLocalValue例外をスローします。
これでステージが設定されました。 「pprint from pprintから」割り当ての形式です。そのため、Pythonは&quot; pprint&quot;というローカル変数を作成します。グローバル変数を隠します。次に、「pprint.pprint」を参照すると、関数内で特別な値をヒットすると、Pythonが例外をスローします。関数にそのimportステートメントがなかった場合、Pythonは通常のlook-in-locals-first-then-look-in-globals解決を使用し、グローバルでpprintモジュールを見つけます。
これを明確にするには、「グローバル」を使用できます。キーワード。もちろん、今ではあなたはすでにあなたの問題を乗り越えて働いています。または他のアプローチが必要な場合。