8

私は最近、このケースに出くわしましたが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解決するのが幸せではない理由を誰かが説明できますか?pprintglobals

編集:良い回答のおかげで、関連する用語で質問を明確にすることができます:

コンパイル時に、識別子pprintはフレームに対してローカルとしてマークされます。実行モデルには、ローカル識別子がバインドされているフレーム内の場所の区別はありませんか? 「このバイトコード命令までグローバルバインディングを参照し、その時点でローカルバインディングに再バインドされている」と言えますか、それとも実行モデルはこれを考慮していませんか?

4

4 に答える 4

6

驚きはどこにありますか?そのスコープ内で再割り当てするスコープに対してグローバルな変数は、コンパイラによってそのスコープに対してローカルとマークされます。

輸入が別の方法で処理されるとしたら、それは驚くべきことです。

ただし、モジュールで使用されているシンボルの後にモジュールの名前を付けない、またはその逆の場合があります。

于 2009-01-01T09:42:23.197 に答える
5

まあ、それは私が少し実験するのに十分興味深く、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() from pprint import pprintは名前がオーバーライドされません。pprintglobalsglobaltwo()

メソッドthree()では、ローカルスコープに名前の宣言がないpprintため、デフォルトpprintでモジュールであるグローバル名になります

ではmain()、最初にキーワードglobal が使用されるpprintため、メソッドのスコープ内のすべての参照は名前main()を参照します。ご覧のとおり、最初はモジュールであり、メソッドでオーバーライドされますglobalpprintglobal namespacefrom pprint import pprint

これは質問の答えではないかもしれませんが、それでも興味深い事実だと思います。

=====================

編集もう一つの興味深いこと。

モジュールがある場合は、次のように言います。

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'

2 番目のインポートが名前空間from mod2 import * の名前をオーバーライドしたため、最初のインポートは無効になりました。datetimeimport datetime

道徳: したがって、インポートの順序、インポートの性質 (x インポート * から)、およびインポートされたモジュール内のインポートの認識が重要です。

于 2009-01-01T08:21:10.003 に答える
4

Python は、コードを実行する前にfrom pprint import pprint、行とマークpprintをローカルの名前として認識しているようです。Python は pprint がローカル変数であるべきだと考えているため、ステートメントで「割り当てる」前にpprint を参照すると、そのエラーがスローされます。 main() pprint.pprint()from..import

それは私がそれを作ることができるのと同じくらい理にかなっています。

もちろん、教訓は、これらのimportステートメントを常にスコープの一番上に置くことです。

于 2009-01-01T06:02:48.583 に答える
4

この質問には数週間前に回答がありましたが、回答を少し明確にすることができると思います。まずいくつかの事実。

1: Python では、

import foo

とほぼ同じです

foo = __import__("foo", globals(), locals(), [], -1)

2: 関数内でコードを実行するときに、Python が関数内でまだ定義されていない変数に遭遇すると、グローバル スコープを調べます。

3: Python には、「ローカル」と呼ばれる関数に使用する最適化があります。Python が関数をトークン化すると、割り当て先のすべての変数が追跡されます。これらの変数のそれぞれに、ローカルの単調に増加する整数からの数値を割り当てます。Python が関数を実行すると、ローカル変数と同じ数のスロットを持つ配列が作成され、各スロットに「まだ割り当てられていない」ことを意味する特別な値が割り当てられ、そこにそれらの変数の値が格納されます。まだ割り当てられていないローカルを参照すると、Python はその特別な値を認識し、UnboundLocalValue 例外をスローします。

これで舞台は整いました。「from pprint import pprint」は、実際には割り当ての形式です。そのため、Python は「pprint」というローカル変数を作成し、グローバル変数を覆い隠します。次に、関数で「pprint.pprint」を参照すると、特別な値にヒットし、Python が例外をスローします。関数にその import ステートメントがない場合、Python は通常の look-in-locals-first-then-look-in-globals 解決を使用し、global で pprint モジュールを見つけます。

これを明確にするために、「global」キーワードを使用できます。もちろん、あなたはすでに問題を解決しており、本当に「グローバル」が必要なのか、それとも他のアプローチが必要なのかはわかりません。

于 2010-01-27T11:25:45.670 に答える