関数でのグローバル変数の使用

2009-01-08 python global-variables scope

関数でグローバル変数を作成または使用するにはどうすればよいですか?

ある関数でグローバル変数を作成した場合、そのグローバル変数を別の関数でどのように使用できますか?アクセスを必要とする関数のローカル変数にグローバル変数を格納する必要がありますか?

Answers

名前空間の概念を調べてみてください。 Pythonでは、 モジュールグローバルデータの自然な場所です。

各モジュールには独自のプライベートシンボルテーブルがあり、モジュールで定義されたすべての関数によってグローバルシンボルテーブルとして使用されます。したがって、モジュールの作成者は、ユーザーのグローバル変数との偶発的な衝突を心配することなく、モジュール内のグローバル変数を使用できます。一方、何をしているのかわかっている場合は、モジュールのグローバル変数を、その関数を参照するために使用するのと同じ表記法modname.itemnameます。

モジュール内のグローバルの特定の使用法をここで説明します- モジュール間でグローバル変数を共有するにはどうすればよいですか? 、そして完全を期すために、ここで内容を共有しています。

単一のプログラム内のモジュール間で情報を共有する標準的な方法は、特別な構成モジュール(しばしばconfigまたはcfgと呼ばれる)を作成することです。アプリケーションのすべてのモジュールに構成モジュールをインポートするだけです。その後、モジュールはグローバル名として使用可能になります。各モジュールのインスタンスは1つしかないため、モジュールオブジェクトに加えられた変更はどこにでも反映されます。例えば:

ファイル:config.py

x = 0   # Default value of the 'x' configuration setting

ファイル:mod.py

import config
config.x = 1

ファイル:main.py

import config
import mod
print config.x

あなたはとしてそれを宣言することにより、他の関数でグローバル変数を使用することができますglobalそれに割り当てる各機能に:

globvar = 0

def set_globvar_to_one():
    global globvar    # Needed to modify global copy of globvar
    globvar = 1

def print_globvar():
    print(globvar)     # No need for global declaration to read value of globvar

set_globvar_to_one()
print_globvar()       # Prints 1

その理由は、グローバル変数が非常に危険であるため、Pythonがglobalキーワードを明示的に要求することで、それが実際に操作していることを確実に確認したいと考えているためだと思います。

モジュール間でグローバル変数を共有する場合は、他の回答を参照してください。

関数でグローバル変数を参照する場合は、 globalキーワードを使用して、どの変数がグローバルであるかを宣言できます。すべてのケースでこれを使用する必要はありません(ここで誰かが誤って主張しているように)-式で参照された名前がローカルスコープまたはこの関数が定義されている関数のスコープで見つからない場合、グローバルから検索されます変数。

ただし、関数でグローバルとして宣言されていない新しい変数に割り当てると、その変数は暗黙的にローカルとして宣言され、同じ名前の既存のグローバル変数に影を付ける可能性があります。

また、グローバル変数は、特にOOPがやりすぎである小さなスクリプトの場合、そうでないと主張する一部のOOP熱狂者とは対照的に、有用です。

私があなたの状況を正しく理解している場合、Pythonがローカル(関数)およびグローバル(モジュール)名前空間を処理する方法の結果が表示されます。

次のようなモジュールがあるとします。

# sample.py
myGlobal = 5

def func1():
    myGlobal = 42

def func2():
    print myGlobal

func1()
func2()

これは42を出力することを期待するかもしれませんが、代わりに5を出力します。すでに述べたように、 func1()に ' global '宣言を追加すると、 func2()は42を出力します。

def func1():
    global myGlobal
    myGlobal = 42

ここで起こっていることは、明示的に指示されない限り、Pythonは、関数内の任意の場所でに割り当てられた名前がその関数に対してローカルであると想定していることです。名前を読み取るだけで、その名前がローカルに存在しない場合は、含まれているスコープ(モジュールのグローバルスコープなど)で名前を検索しようとします。

したがって、 myGlobalという名前に42を割り当てると、Pythonは同じ名前のグローバル変数をシャドウするローカル変数を作成します。そのローカルはスコープ外になり、 func1()が戻るときにガベージコレクションされます。一方、 func2()は、(変更されていない)グローバル名以外は何も表示できません。 (注)この名前空間の決定はない、実行時に、コンパイル時に起こること-あなたはの値の読み取りした場合myGlobal内部func1()あなたがそれに割り当てる前に、あなたが取得したいUnboundLocalError Pythonはすでにそれがなければならないことを決定しましたのでを、ローカル変数ですが、まだ値が関連付けられていません。しかし、 ' global 'ステートメントを使用することで、ローカルに割り当てるのではなく、別の場所で名前を探すようにPythonに指示します。

(この動作は主にローカル名前空間の最適化によって発生したと思います-この動作がない場合、PythonのVMは関数内に新しい名前が割り当てられるたびに少なくとも3つの名前ルックアップを実行する必要があります(名前がtはすでにモジュール/ビルドレベルで存在します)、これは非常に一般的な操作を大幅に遅くします。)

実際にはグローバルをローカル変数に格納するのではなく、元のグローバル参照が参照するのと同じオブジェクトへのローカル参照を作成するだけです。 Pythonのほとんどすべてがオブジェクトを参照する名前であり、通常の操作では何もコピーされないことに注意してください。

識別子が事前定義されたグローバルをいつ参照するかを明示的に指定する必要がなかった場合、おそらく代わりに識別子が新しいローカル変数である場合を明示的に指定する必要があります(たとえば、 'var'コマンドのようなもので) JavaScriptで見られる)。重大で重要なシステムでは、ローカル変数はグローバル変数よりも一般的であるため、ほとんどの場合、Pythonのシステムの方が理にかなっています。

あなたはそれが存在していたか、それがなかった場合は、ローカル変数を作成する場合は、グローバル変数を使用して、推測しようとした言語を持つことができます 。ただし、これは非常にエラーが発生しやすくなります。たとえば、別のモジュールをインポートすると、その名前のグローバル変数が誤って導入され、プログラムの動作が変更される可能性があります。

Pythonは単純なヒューリスティックを使用して、ローカルとグローバルの間で、変数をロードするスコープを決定します。変数名が割り当ての左側に表示されているが、グローバルとして宣言されていない場合は、ローカルであると見なされます。割り当ての左側に表示されない場合は、グローバルと見なされます。

>>> import dis
>>> def foo():
...     global bar
...     baz = 5
...     print bar
...     print baz
...     print quux
... 
>>> dis.disassemble(foo.func_code)
  3           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (baz)

  4           6 LOAD_GLOBAL              0 (bar)
              9 PRINT_ITEM          
             10 PRINT_NEWLINE       

  5          11 LOAD_FAST                0 (baz)
             14 PRINT_ITEM          
             15 PRINT_NEWLINE       

  6          16 LOAD_GLOBAL              1 (quux)
             19 PRINT_ITEM          
             20 PRINT_NEWLINE       
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE        
>>> 

foo()割り当ての左側に表示されるbazが唯一のLOAD_FAST変数であることを確認してください。

並列実行では、何が起こっているのか理解していない場合、グローバル変数が予期しない結果を引き起こす可能性があります。マルチプロセッシング内でグローバル変数を使用する例を次に示します。各プロセスが独自の変数のコピーで機能することがはっきりとわかります。

import multiprocessing
import os
import random
import sys
import time

def worker(new_value):
    old_value = get_value()
    set_value(random.randint(1, 99))
    print('pid=[{pid}] '
          'old_value=[{old_value:2}] '
          'new_value=[{new_value:2}] '
          'get_value=[{get_value:2}]'.format(
          pid=str(os.getpid()),
          old_value=old_value,
          new_value=new_value,
          get_value=get_value()))

def get_value():
    global global_variable
    return global_variable

def set_value(new_value):
    global global_variable
    global_variable = new_value

global_variable = -1

print('before set_value(), get_value() = [%s]' % get_value())
set_value(new_value=-2)
print('after  set_value(), get_value() = [%s]' % get_value())

processPool = multiprocessing.Pool(processes=5)
processPool.map(func=worker, iterable=range(15))

出力:

before set_value(), get_value() = [-1]
after  set_value(), get_value() = [-2]
pid=[53970] old_value=[-2] new_value=[ 0] get_value=[23]
pid=[53971] old_value=[-2] new_value=[ 1] get_value=[42]
pid=[53970] old_value=[23] new_value=[ 4] get_value=[50]
pid=[53970] old_value=[50] new_value=[ 6] get_value=[14]
pid=[53971] old_value=[42] new_value=[ 5] get_value=[31]
pid=[53972] old_value=[-2] new_value=[ 2] get_value=[44]
pid=[53973] old_value=[-2] new_value=[ 3] get_value=[94]
pid=[53970] old_value=[14] new_value=[ 7] get_value=[21]
pid=[53971] old_value=[31] new_value=[ 8] get_value=[34]
pid=[53972] old_value=[44] new_value=[ 9] get_value=[59]
pid=[53973] old_value=[94] new_value=[10] get_value=[87]
pid=[53970] old_value=[21] new_value=[11] get_value=[21]
pid=[53971] old_value=[34] new_value=[12] get_value=[82]
pid=[53972] old_value=[59] new_value=[13] get_value=[ 4]
pid=[53973] old_value=[87] new_value=[14] get_value=[70]

結局のところ、答えは常に単純です。

以下は、 main定義でそれを示す簡単な方法を持つ小さなサンプルモジュールです。

def five(enterAnumber,sumation):
    global helper
    helper  = enterAnumber + sumation

def isTheNumber():
    return helper

これをmain定義で表示する方法は次のとおりです。

import TestPy

def main():
    atest  = TestPy
    atest.five(5,8)
    print(atest.isTheNumber())

if __name__ == '__main__':
    main()

この単純なコードはそのように機能し、実行されます。お役に立てば幸いです。

既存の回答に加えて、これをさらに混乱させるために:

Pythonでは、関数内でのみ参照される変数は 暗黙的にグローバル 。変数にどこかに新しい値が割り当てられている場合 関数の本体内では、 ローカルであると見なされます。変数の場合 関数内で新しい値が割り当てられると、変数は 暗黙的にローカルであり、明示的に「グローバル」として宣言する必要があります。

最初は少し意外ですが、一瞬の考察で説明します この。一方、割り当てられた変数にグローバルを要求すると、 意図しない副作用を防ぎます。一方、グローバルが すべてのグローバル参照に必要です。すべてのグローバル参照を使用します。 時間。組み込みへのすべての参照をグローバルとして宣言する必要があります 関数またはインポートされたモジュールのコンポーネント。この混乱は 特定のためのグローバル宣言の有用性を打ち負かす 副作用。

出典: Pythonのローカル変数とグローバル変数のルールは何ですか?

あなたが言っているのは、このような方法を使うことです:

globvar = 5

def f():
    var = globvar
    print(var)

f()  # Prints 5

しかし、より良い方法は、次のようにグローバル変数を使用することです。

globavar = 5
def f():
    global globvar
    print(globvar)
f()   #prints 5

どちらも同じ出力になります。

使用するすべての関数でグローバル変数を参照する必要があります。

次のように:

var = "test"

def printGlobalText():
    global var #wWe are telling to explicitly use the global version
    var = "global from printGlobalText fun."
    print "var from printGlobalText: " + var

def printLocalText():
    #We are NOT telling to explicitly use the global version, so we are creating a local variable
    var = "local version from printLocalText fun"
    print "var from printLocalText: " + var

printGlobalText()
printLocalText()
"""
Output Result:
var from printGlobalText: global from printGlobalText fun.
var from printLocalText: local version from printLocalText
[Finished in 0.1s]
"""

これを試して:

def x1():
    global x
    x = 6

def x2():
    global x
    x = x+1
    print x

x = 5
x1()
x2()  # output --> 7

その後、アドオンとして、ファイルを使用して、すべてローカルで宣言されたすべてのグローバル変数を格納し、次のようにimport asます。

ファイルinitval.py

Stocksin = 300
Prices = []

ファイルgetstocks.py

import initval as iv

def getmystocks(): 
    iv.Stocksin = getstockcount()


def getmycharts():
    for ic in range(iv.Stocksin):

ある関数でグローバル変数を作成した場合、その変数を別の関数でどのように使用できますか?

次の関数を使用してグローバルを作成できます。

def create_global_variable():
    global global_variable # must declare it to be a global first
    # modifications are thus reflected on the module's global scope
    global_variable = 'Foo' 

関数を作成しても、実際にはそのコードは実行されません。したがって、 create_global_variable関数を呼び出します。

>>> create_global_variable()

変更せずにグローバルを使用する

それが指すオブジェクトを変更することを期待しない限り、それを使用することができます:

例えば、

def use_global_variable():
    return global_variable + '!!!'

そして今、私たちはグローバル変数を使うことができます:

>>> use_global_variable()
'Foo!!!'

関数内からのグローバル変数の変更

別のオブジェクトでグローバル変数をポイントするには、もう一度グローバルキーワードを使用する必要があります。

def change_global_variable():
    global global_variable
    global_variable = 'Bar'

この関数を記述した後、実際に関数を変更するコードはまだ実行されていないことに注意してください。

>>> use_global_variable()
'Foo!!!'

したがって、関数を呼び出した後:

>>> change_global_variable()

グローバル変数が変更されていることがわかります。 global_variable名が'Bar'指すようになりました。

>>> use_global_variable()
'Bar!!!'

Pythonの「グローバル」は真にグローバルではないことに注意してください-モジュールレベルに対してのみグローバルです。したがって、グローバルなモジュールで記述された関数でのみ使用できます。関数は、それらが記述されているモジュールを記憶しているため、他のモジュールにエクスポートされても、グローバル変数を見つけるために、それらが作成されたモジュールを調べます。

同じ名前のローカル変数

同じ名前のローカル変数を作成すると、グローバル変数に影が付きます。

def use_local_with_same_name_as_global():
    # bad name for a local variable, though.
    global_variable = 'Baz' 
    return global_variable + '!!!'

>>> use_local_with_same_name_as_global()
'Baz!!!'

しかし、その誤った名前のローカル変数を使用しても、グローバル変数は変更されません。

>>> use_global_variable()
'Bar!!!'

何をしているのかを正確に理解していて、それを行う十分な理由がない限り、グローバルと同じ名前のローカル変数の使用は避けてください。私はまだそのような理由に遭遇していません。

クラスでも同じ動作が得られます

コメントのフォローは尋ねます:

クラス内の関数内にグローバル変数を作成し、別のクラス内の別の関数内でその変数を使用したい場合はどうすればよいですか?

ここでは、通常の関数と同じようにメソッドで動作することを示します。

class Foo:
    def foo(self):
        global global_variable
        global_variable = 'Foo'

class Bar:
    def bar(self):
        return global_variable + '!!!'

Foo().foo()

そして今:

>>> Bar().bar()
'Foo!!!'

ただし、モジュールの名前空間が混乱しないように、グローバル変数を使用する代わりにクラス属性を使用することをお勧めします。また、ここではself引数を使用しないことに注意してください。これらはクラスメソッド(通常のcls引数からクラス属性を変更する場合に便利)または静的メソッド( selfまたはcls )である可能性があります。

グローバル配列の明示的な要素への書き込みは、グローバル宣言を必要としないようですが、「卸売」への書き込みにはその要件があります。

import numpy as np

hostValue = 3.14159
hostArray = np.array([2., 3.])
hostMatrix = np.array([[1.0, 0.0],[ 0.0, 1.0]])

def func1():
    global hostValue    # mandatory, else local.
    hostValue = 2.0

def func2():
    global hostValue    # mandatory, else UnboundLocalError.
    hostValue += 1.0

def func3():
    global hostArray    # mandatory, else local.
    hostArray = np.array([14., 15.])

def func4():            # no need for globals
    hostArray[0] = 123.4

def func5():            # no need for globals
    hostArray[1] += 1.0

def func6():            # no need for globals
    hostMatrix[1][1] = 12.

def func7():            # no need for globals
    hostMatrix[0][0] += 0.33

func1()
print "After func1(), hostValue = ", hostValue
func2()
print "After func2(), hostValue = ", hostValue
func3()
print "After func3(), hostArray = ", hostArray
func4()
print "After func4(), hostArray = ", hostArray
func5()
print "After func5(), hostArray = ", hostArray
func6()
print "After func6(), hostMatrix = \n", hostMatrix
func7()
print "After func7(), hostMatrix = \n", hostMatrix

同じ名前のローカル変数がある場合は、 globals()関数を使用できます

globals()['your_global_var'] = 42

変更を表示するクラス名前空間を参照します。

この例では、ランナーはファイル構成のmaxを使用しています。ランナーが使用しているときにテストでmaxの値を変更したい。

main / config.py

max = 15000

main / runner.py

from main import config
def check_threads():
    return max < thread_count 

tests / runner_test.py

from main import runner                # <----- 1. add file
from main.runner import check_threads
class RunnerTest(unittest):
   def test_threads(self):
       runner.max = 0                  # <----- 2. set global 
       check_threads()

他のどの回答でも見たことがないので、これを追加します。これは、似たような問題に苦しんでいる人に役立つかもしれません。 globals()関数は、変更可能なグローバルシンボルディクショナリを返します。これにより、残りのコードでデータを「魔法のように」使用できるようになります。 例えば:

from pickle import load
def loaditem(name):
    with open(r"C:\pickle\file\location"+"\{}.dat".format(name), "rb") as openfile:
        globals()[name] = load(openfile)
    return True

そして

from pickle import dump
def dumpfile(name):
    with open(name+".dat", "wb") as outfile:
        dump(globals()[name], outfile)
    return True

変数をグローバル名前空間にダンプ/ロードするだけです。非常に便利で、大騒ぎせず、大騒ぎしません。 Python 3のみであることを確認してください。

グローバルは問題ありません-マルチプロセッシングを除く

異なるプラットフォーム/環境でのマルチプロセッシングに関連するグローバル 一方はWindows / Mac OS、もう一方はLinuxが面倒だからです。

これを、前に遭遇した問題を指摘する簡単な例で示します。

あなたが理解したいなら、なぜ物事がWindows / MacOsとLinuxで違うのか 新しいプロセスを開始するデフォルトのメカニズムを知る必要があります...

  • Windows / MacOは「スポーン」
  • Linuxは「フォーク」です

彼らはメモリの割り当てと初期化が異なります...(しかし私はこれには入りません ここに)。

問題/例を見てみましょう...

import multiprocessing

counter = 0

def do(task_id):
    global counter
    counter +=1
    print(f'task {task_id}: counter = {counter}')

if __name__ == '__main__':

    pool = multiprocessing.Pool(processes=4)
    task_ids = list(range(4))
    pool.map(do, task_ids)

ウィンドウズ

これをWindowsで実行すると(私はMacOSでもそうだと思います)、次の出力が得られます...

task 0: counter = 1
task 1: counter = 2
task 2: counter = 3
task 3: counter = 4

Linux

これをLinuxで実行すると、代わりに次のようになります。

task 0: counter = 1
task 1: counter = 1
task 2: counter = 1
task 3: counter = 1

Related