モジュール内にグローバル変数を設定する方法はありますか?以下に示すように最も明白な方法でそれを行おうとしたとき、Pythonインタープリターは変数__DBNAME__
が存在しないと言った。
...
__DBNAME__ = None
def initDB(name):
if not __DBNAME__:
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
...
そして、別のファイルにモジュールをインポートした後
...
import mymodule
mymodule.initDB('mydb.sqlite')
...
そしてトレースバックは:UnboundLocalError: local variable '__DBNAME__' referenced before assignment
何か案は? このフェローの 推奨に従って、モジュールを使用してシングルトンを設定しようとしています。
これが何が起こっているかです。
最初に、Pythonが実際に持つ唯一のグローバル変数は、モジュールスコープの変数です。真にグローバルな変数を作成することはできません。特定のスコープで変数を作成するだけです。 (Pythonインタープリター内で変数を作成し、他のモジュールをインポートする場合、変数は最も外側のスコープにあるため、Pythonセッション内でグローバルになります。)
モジュールグローバル変数を作成するために必要なことは、名前に割り当てるだけです。
この1行を含むfoo.pyというファイルを想像してください。
X = 1
それをインポートすると想像してください。
import foo
print(foo.X) # prints 1
ただし、例のように、モジュールスコープ変数の1つを関数内のグローバルとして使用するとします。 Pythonのデフォルトでは、関数変数はローカルであると想定されています。グローバルを使用する前に、関数にglobal
宣言を追加するだけです。
def initDB(name):
global __DBNAME__ # add this line!
if __DBNAME__ is None: # see notes below; explicit test for None
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
ちなみに、この例では、空の文字列以外の文字列値はtrueと評価されるため、実際のデータベース名はtrueと評価されるため、単純なif not __DBNAME__
テストで十分です。しかし、0の可能性のある数値を含む可能性のある変数の場合、単にif not variablename
とは言えません。その場合は、None
演算子を使用してis
を明示的にテストする必要があります。サンプルを変更して、明示的なNone
テストを追加しました。 None
の明示的なテストは決して間違っていないため、デフォルトで使用します。
最後に、他の人がこのページで指摘したように、2つの先頭のアンダースコアは、変数をモジュールに対して「プライベート」にしたいというPythonに信号を送ります。 import * from mymodule
を実行した場合、Pythonは、名前空間に2つの先行アンダースコアを含む名前をインポートしません。ただし、単純なimport mymodule
を実行してからdir(mymodule)
と言うと、リストに「プライベート」変数が表示され、mymodule.__DBNAME__
を明示的に参照する場合はPythonは関係ありませんが、参照するだけですそれ。二重の先頭のアンダースコアは、モジュールのユーザーにとって、その名前を自分の値に再バインドしたくないという大きな手がかりです。
Pythonでは、import *
ではなく、mymodule.something
を使用するか、from mymodule import something
のようなインポートを明示的に行うことにより、結合を最小化し、明示性を最大にすることがベストプラクティスと見なされます。
編集:何らかの理由で、global
キーワードを持たない非常に古いバージョンのPythonでこのようなことをする必要がある場合、簡単な回避策があります。モジュールのグローバル変数を直接設定する代わりに、モジュールのグローバルレベルで可変タイプを使用し、その中に値を保存します。
関数では、グローバル変数名は読み取り専用になります。実際のグローバル変数名を再バインドすることはできません。 (関数内でその変数名に割り当てた場合、関数内のローカル変数名にのみ影響します。)しかし、そのローカル変数名を使用して実際のグローバルオブジェクトにアクセスし、その中にデータを保存できます。
list
を使用できますが、コードは見苦しくなります。
__DBNAME__ = [None] # use length-1 list as a mutable
# later, in code:
if __DBNAME__[0] is None:
__DBNAME__[0] = name
dict
の方が優れています。しかし、最も便利なのはクラスインスタンスであり、単純なクラスを使用できます。
class Box:
pass
__m = Box() # m will contain all module-level values
__m.dbname = None # database name global in module
# later, in code:
if __m.dbname is None:
__m.dbname = name
(データベース名変数を大文字にする必要はありません。)
私は__m.dbname
ではなく__m["DBNAME"]
を使用するだけの構文糖が好きです。私の意見では最も便利な解決策のようです。ただし、dict
ソリューションも正常に機能します。
dict
を使用すると、ハッシュ可能な値をキーとして使用できますが、有効な識別子である名前に満足している場合は、上記のBox
のような簡単なクラスを使用できます。
モジュール上で明示的にアクセスすることによるモジュールレベル変数への明示的アクセス
要するに:ここで説明する手法は stevehaの答え 、except、変数を明示的にスコープするための人工ヘルパーオブジェクトは作成されません。 代わりに、モジュールオブジェクト自体に変数ポインタが与えられるため、どこからでもアクセスすると明示的なスコープを提供します。 (ローカル関数スコープでの割り当てなど)。
selffor現在のインスタンスの代わりに、現在のモジュール!
# db.py
import sys
# this is a pointer to the module object instance itself.
this = sys.modules[__name__]
# we can explicitly make assignments on it
this.db_name = None
def initialize_db(name):
if (this.db_name is None):
# also in local function scope. no scope specifier like global is needed
this.db_name = name
# also the name remains free for local use
db_name = "Locally scoped db_name variable. Doesn't do anything here."
else:
msg = "Database is already initialized to {0}."
raise RuntimeError(msg.format(this.db_name))
モジュールがキャッシュされるため、インポートは1回のみ であるため、db.py
を必要なだけ多くのクライアントにインポートして、同じ普遍的な状態を操作できます。
# client_a.py
import db
db.initialize_db('mongo')
# client_b.py
import db
if (db.db_name == 'mongo'):
db.db_name = None # this is the preferred way of usage, as it updates the value for all clients, because they access the same reference from the same module object
# client_c.py
from db import db_name
# be careful when importing like this, as a new reference "db_name" will
# be created in the module namespace of client_c, which points to the value
# that "db.db_name" has at import time of "client_c".
if (db_name == 'mongo'): # checking is fine if "db.db_name" doesn't change
db_name = None # be careful, because this only assigns the reference client_c.db_name to a new value, but leaves db.db_name pointing to its current value.
追加のボーナスとして、Pythonのポリシーよりも暗黙的よりも優れているため、全体的に非常にPythonicであることがわかりました。
スティーブハの答えは私にとっては有益でしたが、重要な点(私は気の利いたと思う点)を省略しています。関数で変数にアクセスするだけで変数を割り当てない場合、globalキーワードは必要ありません。
Globalキーワードなしで変数を割り当てると、Pythonは新しいローカル変数を作成します。モジュール変数の値は関数内に隠されます。関数内でモジュールvarを割り当てるには、globalキーワードを使用します。
Python 2.7のPylint 1.3.1では、変数を割り当てない場合、グローバルを使用しないように強制します。
module_var = '/dev/hello'
def readonly_access():
connect(module_var)
def readwrite_access():
global module_var
module_var = '/dev/hello2'
connect(module_var)
そのためには、変数をグローバルとして宣言する必要があります。ただし、グローバル変数には、module_name.var_name
を使用してoutsideからもアクセスできます。これをモジュールの最初の行として追加します。
global __DBNAME__