7

グローバル変数を変更する 3 つの関数を定義しましたx

def changeXto1():
    global x
    x = 1

def changeXto2():
    from __main__ import x
    x = 2

def changeXto3():
    import __main__
    __main__.x = 3

x = 0
print x
changeXto1()
print x
changeXto2()
print x
changeXto3()
print x

結果は次のとおりです。

0
1
1
3

changeXto1通常のグローバル ステートメントを使用します。結果は期待どおり== 1xです。これはうまくいきません。その後も 1.を使用して経由でアドレス指定します。その後の結果は予想通り3です。changeXto2from __main__ importxxchangeXto3import mainx__main__.x

で動作しているのに、でfrom __main__ import動作しないのはなぜですか? モジュールでもグローバル変数に対処できるのに、Python でグローバル ステートメントが必要なのはなぜですか?changeXto2import __main__changeXto3__main__

4

2 に答える 2

10

これは、Python がコードをバイトコードに変換する方法 (コンパイル手順) に関連しています。

関数をコンパイルするとき、Python は割り当てられたすべての変数をローカル変数として扱い、最適化を実行して名前の検索回数を減らします。各ローカル変数にはインデックスが割り当てられ、関数が呼び出されると、その値はインデックスでアドレス指定されたスタック ローカル配列に格納されます。コンパイラは、変数にアクセスするためのオペコードを出力しますLOAD_FASTSTORE_FAST

代わりに、global構文は、変数に値が割り当てられている場合でも、ローカル変数と見なすべきではなく、インデックスを割り当てるべきではないことをコンパイラに示します。代わりにLOAD_GLOBALSTORE_GLOBALオペコードを使用して変数にアクセスします。これらのオペコードは、おそらく多くの辞書 (ローカル、グローバル) で検索を行うために名前を使用するため、処理が遅くなります。

LOAD_GLOBAL変数が値を読み取るためだけにアクセスされる場合、コンパイラはそれがローカル変数であるかグローバル変数であるかがわからないため、常に出力し、グローバル変数であると想定します。

したがって、最初の関数で using を使用すると、書き込みアクセスをローカル変数ではなくグローバル変数への書き込みとしてglobal x処理するようにコンパイラーに通知します。x関数のオペコードはそれを明確にします:

>>> dis.dis(changeXto1)
  3           0 LOAD_CONST               1 (1)
              3 STORE_GLOBAL             0 (x)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        

__main__3 番目の例では、モジュールをという名前のローカル変数にインポートして__main__から、そのxフィールドに代入します。モジュールはすべての最上位マッピングをフィールドとして格納するオブジェクトであるためx__main__モジュール内の変数に割り当てています。ご覧のとおり、コードはモジュールで定義されているため、__main__モジュール フィールドはディクショナリの値に直接マップされます。オペコードは、直接アクセスしないことを示しています。globals()__main__x

>>> dis.dis(changeXto3)
  2           0 LOAD_CONST               1 (-1)
              3 LOAD_CONST               0 (None)
              6 IMPORT_NAME              0 (__main__)
              9 STORE_FAST               0 (__main__)

  3          12 LOAD_CONST               2 (3)
             15 LOAD_FAST                0 (__main__)
             18 STORE_ATTR               1 (x)
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE        

2 番目の例は興味深いものです。変数に値を割り当てるためx、コンパイラはそれがローカル変数であると想定し、最適化を行います。次に、はモジュールをfrom __main__ import xインポートし、モジュール内のの値をという名前のローカル変数に__main__バインドする新しいバインディングを作成します。これは常に当てはまります。現在の名前空間に新しいバインディングを作成するだけです。変数に新しい値を代入すると、関連のないモジュール内のバインディングではなく、現在のバインディングが変更されるだけです(ただし、値が変更可能で、それを変更すると、変更はすべてのバインディングを通じて表示されます)。オペコードは次のとおりです。x__main__xfrom ${module} import ${name}x__main__

>>> dis.dis(f2)
  2           0 LOAD_CONST               1 (-1)
              3 LOAD_CONST               2 (('x',))
              6 IMPORT_NAME              0 (__main__)
              9 IMPORT_FROM              1 (x)
             12 STORE_FAST               0 (x)
             15 POP_TOP             

  3          16 LOAD_CONST               3 (2)
             19 STORE_FAST               0 (x)
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE        

これについて考える良い方法は、Python ではすべての割り当てが名前を辞書内の値にバインドし、逆参照は単に辞書検索を行うことです (これは大まかな概算ですが、概念モデルにかなり近いものです)。を実行すると、キーobj.fieldの隠し辞書obj( 経由でアクセス可能obj.__dict__) が検索され"field"ます。

ネイキッド変数名がある場合は、locals()辞書で検索され、globals()異なる場合は辞書で検索されます (コードがモジュール レベルで実行される場合は同じです)。locals()割り当ての場合、グローバル アクセスが必要であると宣言しない限り、バインディングは常にディクショナリに配置されますglobal ${name}(この構文はトップレベルでも機能します)。

したがって、関数を翻訳すると、これはほとんど次のようになります。

# NOTE: this is valid Python code, but is less optimal than
# the original code. It is here only for demonstration.

def changeXto1():
    globals()['x'] = 1

def changeXto2():
    locals()['x'] = __import__('__main__').__dict__['x']
    locals()['x'] = 2

def changeXto3():
    locals()['__main__'] = __import__('__main__')
    locals()['__main__'].__dict__['x'] = 3
于 2013-02-16T11:11:36.077 に答える
8

Why doesn't from __main__ import work in changeXto2, while import __main__ is working in changeXto3?

It works fine, it just doesn't do what you want. It copies the name and value into the local namespace instead of having the code access __main__'s namespace.

Why do we need a global statement in Python if we can address global variables also with the __main__ module?

Because they only do the same thing when your code is running in __main__. If you're running in, say, othermodule after importing it, then __main__ will refer to the main script and not othermodule.

于 2013-02-16T10:35:11.737 に答える