1. ホーム
  2. パイソン

[解決済み】Pythonでモジュール全体の変数を作成する方法は?[重複している]をクリックします。

2022-04-03 17:15:15

質問

モジュール内部でグローバル変数を設定する方法はありますか?一番わかりやすい方法でやろうとすると、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: ローカル変数 ' DBNAME 代入前に参照された ...

何かアイデアはありますか?私は、以下のように、モジュールを使用してシングルトンをセットアップしようとしています。 こいつの を推奨しています。

解決方法は?

以下はその内容です。

まず、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.")

ちなみに、この例では、単純な if not __DBNAME__ なぜなら、空文字列以外の文字列値はすべて真と評価されるからです。したがって、実際のデータベース名はすべて真と評価されます。 しかし、0かもしれない数値の値を含む可能性のある変数については、単に if not variablename その場合は、明示的に None を使用しています。 is 演算子を使用します。 私はこの例を修正して、明示的な None のテストを行います。 の明示的なテストは None は決して間違っていないので、私はデフォルトでそれを使っています。

最後に、このページで他の人が指摘しているように、先頭の2つのアンダースコアは、変数をモジュールに対して "private"にしたいことを Python に通知します。 もし、あなたが import * from mymodule Pythonは先頭のアンダースコアが2つある名前を名前空間にインポートしません。 しかし、もしあなたが単純な import mymodule と言ってから dir(mymodule) を明示的に参照すると、リストの中に private" 変数が表示されます。 mymodule.__DBNAME__ Pythonはそれを気にせず、ただ参照させるだけです。 先頭の2つのアンダースコアは、モジュールのユーザがその名前を自分自身の値にバインドし直さないようにするための大きな手がかりとなります。

Pythonのベストプラクティスとされているのが import * を使うことで、結合を最小化し、明示性を最大化することができます。 mymodule.something のようなインポートを明示的に行うか from mymodule import something .

EDIT: もし何らかの理由で、非常に古いバージョンのPythonでこのようなことをする必要がある場合、そのPythonには global キーワードを使えば、簡単に回避することができます。 モジュールグローバル変数を直接設定する代わりに、モジュールグローバルレベルでmutable型を使用し、その中に値を格納します。

関数内では、グローバル変数名は読み取り専用になり、実際のグローバル変数名をリバインドすることはできません。 (関数内でその変数名に代入すると、関数内のローカル変数名にのみ影響します)。 しかし、そのローカル変数名を使って、実際のグローバルオブジェクトにアクセスし、その中にデータを格納することは可能です。

を使用することができます。 list しかし、あなたのコードは醜いものになるでしょう。

__DBNAME__ = [None] # use length-1 list as a mutable

# later, in code:  
if __DBNAME__[0] is None:
    __DBNAME__[0] = name

A 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 を上記のようにします。