クロスモジュール変数を作成するにはどうすればよいですか?


122

__debug__それはすべてのモジュールに影響を与えるため、変数は、一部で便利です。同じように機能する別の変数を作成する場合、どうすればよいですか?

変数(オリジナルで 'foo'としましょう)は、あるモジュールでfooを変更すると、他のモジュールでも更新されるという意味で、本当にグローバルである必要はありません。他のモジュールをインポートする前にfooを設定できれば、同じ値が表示されます。

回答:


114

私はこのソリューションを形や形で保証するものではありません。ただし、__builtin__モジュールに変数を追加__builtin__すると、デフォルトでは、それを含む他のモジュールのグローバルであるかのようにアクセスできます。

a.pyには

print foo

b.pyには

import __builtin__
__builtin__.foo = 1
import a

その結果、「1」が出力されます。

編集:__builtin__モジュールはローカルシンボルとして利用可能である__builtins__-これらの答えの二つの間の不一致の理由です。また、python3 __builtin__ではに名前が変更さbuiltinsれています。


2
なんらかの理由で、この状況が気に入らないのですか?
ソフトウェア熱狂的な2010

31
一つには、コードを読んでいるときの人々の期待を壊します。「ここで使用されているこの「foo」記号は何ですか?それがどこに定義されているのかわからないのですか?」
Curt Hagenlocher、2010

9
また、Pythonの将来のバージョンが実際に組み込みとして選択した名前を使用し始めると、大混乱を引き起こす可能性があります。
2010年

4
これは、インポートされたモジュールとdb接続を共有する場合などに適したソリューションです。健全性チェックとして、インポートされたモジュールがアサートすることを確認しますhasattr(__builtin__, "foo")
マイクエリス

4
この答えを読んでいる人のために:DONT!行う !この ! 本当に、しないでください。
Bruno desthuilliers

161

グローバルなクロスモジュール変数が必要な場合は、単純なグローバルモジュールレベルの変数で十分な場合があります。

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

テスト:

$ python b.py
# -> 1 2

実際の例:Djangoのglobal_settings.py(ただし、Djangoアプリではオブジェクトの インポートによって設定が使用されますdjango.conf.settings)。


3
名前空間の競合の可能性を回避できるため、より良い
bgw 2010年

インポートするモジュール(この場合a.py)に含まれている場合はmain()どうなりますか?それは重要ですか?
sedeh 2014

4
@sedeh:いいえ。a.pyもスクリプトとして実行されている場合は、if __name__=="__main__"ガードでガードを使用して、インポート時に予期しないコードが実行されるのを防ぎます。
jfs 14

6
現実の世界では、このソリューションには少し注意する必要があります。プログラマーが「インポート変数から」を使用して「グローバル」変数を取得する場合(c.pyでこのバリエーションを試してください)、インポート時に変数のコピーを取得します。
ポールウィップ2015

1
@PaulWhipp:間違っている:(ヒント使用がid()身元を確認する)
JFS

25

それが意味をなす多くの状況があり、いくつかの(密に結合された)モジュール間で既知のいくつかのグローバルを持つようにプログラミングを単純化すると私は信じています。この精神の中で、それらを参照する必要のあるモジュールによってインポートされるグローバルのモジュールを持つというアイデアについて少し詳しく説明したいと思います。

そのようなモジュールが1つしかない場合は、「g」という名前を付けます。その中で、グローバルとして扱う予定のすべての変数にデフォルト値を割り当てます。それらのいずれかを使用する各モジュールでは、「from g import var」を使用しません。これは、インポート時にのみgから初期化されるローカル変数のみが生成されるためです。ほとんどの参照はg.varの形式で行い、「g」を使用します。他のモジュールからアクセスできる可能性のある変数を扱っていることを常に思い出させてくれます。

このようなグローバル変数の値がモジュール内の関数で頻繁に使用される場合、その関数はローカルコピーを作成できます:var = g.var。ただし、varへの割り当てはローカルであり、グローバルg.varは割り当てでg.varを明示的に参照しないと更新できないことを理解することが重要です。

モジュールのさまざまなサブセットによって共有されるそのような複数のグローバルモジュールを使用して、状況をもう少し厳しく制御することもできます。私のグローバルモジュールに短い名前を使用する理由は、それらの出現によってコードが乱雑になりすぎないようにするためです。ほんの少しの経験で、彼らはたった1つか2つのキャラクターで十分ニーモニックになります。

たとえば、xがgでまだ定義されていない場合でもgxへの割り当てを行うことは可能であり、別のモジュールがgxにアクセスできます。それ。割り当ての変数名のタイプミスの結果として、誤ってgに新しい変数を作成する可能性があります。dir(g)の調査は、そのような事故によって生じた可能性のある驚きの名前を見つけるのに役立つことがあります。


7
この興味深い観察は私の問題を解決しました:「インポート時にのみgから初期化されるローカル変数のみが生成されるため、「from g import var」を使用しません。」"from..import"は "import"と同じであると想定するのが妥当と思われますが、これは正しくありません。
Curtis Yallop、

24

モジュールを定義し( "globalbaz"と呼びます)、その中に変数を定義します。この「pseudoglobal」を使用するすべてのモジュールは「globalbaz」モジュールをインポートし、「globalbaz.var_name」を使用してそれを参照する必要があります

これは変更の場所に関係なく機能し、インポートの前または後に変数を変更できます。インポートされたモジュールは最新の値を使用します。(私はおもちゃの例でこれをテストしました)

明確にするために、globalbaz.pyは次のようになります。

var_name = "my_useful_string"

9

1つのモジュールのグローバルを別のモジュールに渡すことができます。

モジュールA:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

モジュールB:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3

7

グローバル変数は通常悪い考えですが、次のように割り当てることでこれを行うことができます__builtins__

__builtins__.foo = 'something'
print foo

また、モジュール自体は、任意のモジュールからアクセスできる変数です。だからと呼ばれるモジュールを定義した場合my_globals.py

# my_globals.py
foo = 'something'

次に、それをどこからでも使用できます。

import my_globals
print my_globals.foo

変更するので__builtins__はなくモジュールを使用する方が、この種のグローバルを実行するためのよりクリーンな方法です。


3
__builtins__CPythonの癖があり、あなたが実際にそれを使うべきではありません-より良い使用__builtin__(またはbuiltinsなどのpython3で)受け入れ答えのショー
トビアスKienzler

5

これは、モジュールレベルの変数ですでに実行できます。モジュールは、インポート元のモジュールに関係なく同じです。したがって、変数を、それを配置する意味のある任意のモジュールでモジュールレベルの変数にして、他のモジュールからアクセスまたは割り当てることができます。変数の値を設定するために関数を呼び出すか、またはいくつかのシングルトンオブジェクトのプロパティにすることをお勧めします。そうすれば、変数が変更されたときにコードを実行する必要が生じた場合でも、モジュールの外部インターフェイスを壊すことなく実行できます。

これは通常、物事を行うための優れた方法ではありません—グローバルを使用することはめったにありません—が、これが最もクリーンな方法だと思います。


3

変数が見つからない場合があるとの回答を投稿したいと思いました。

循環インポートはモジュールの動作を壊す可能性があります。

例えば:

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

これは例ですが、明らかなはずですが、大規模なコードベースでは、これは本当に混乱する可能性があります。


1

これは、__builtin__名前空間を変更するように聞こえます。それをするために:

import __builtin__
__builtin__.foo = 'some-value'

__builtins__直接使用しないでください(追加の「s」に注意してください)-明らかにこれは辞書またはモジュールの可能性があります。これを指摘してくれたΤΖΩΤΖΙΟΥのおかげで、もっと多くがここにあります

今、fooどこでも使用可能です。

これを一般的に行うことはお勧めしませんが、これを使用するかどうかはプログラマ次第です。

これへの割り当ては上記のように行う必要がありfoo = 'some-other-value'ます。設定すると、現在のネームスペースにのみ設定されます。


1
(comp.lang.pythonから)直接ビルトインを使用することは避けるべきだと覚えています。代わりに、Curt Hagenlocherが示唆したように、組み込みをインポートして使用します。
tzot 2008

1

私は、これが本当に欠けていると感じたいくつかの組み込みのプリミティブ関数に使用します。1つの例は、filter、map、reduceと同じ使用法セマンティクスを持つfind関数です。

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

これを実行すると(たとえば、エントリポイントの近くにインポートすることにより)、すべてのモジュールでfind()を使用できるようになります。

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

注:もちろん、これを行うには、フィルターと別のラインを使用して長さがゼロかどうかをテストするか、ある種の奇妙なラインを縮小しますが、いつも変だと感じていました。


1

辞書を使用して、モジュール間で変更可能な(または変更可能な)変数を実現できます。

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

の起動test_wait_app_up_fail時の実際のタイムアウト時間は3秒です。


1

変数の値を渡すためにグローバル/モジュール名前空間ではなくクラス名前空間を使用することにより、グローバル変数(http://wiki.c2.com/?GlobalVariablesAreBadを参照)を使用することのいくつかの欠点を回避できるか。次のコードは、2つの方法が基本的に同じであることを示しています。以下で説明するように、クラス名前空間の使用には若干の利点があります。

次のコードフラグメントは、グローバルまたはモジュールの名前空間とクラスの名前空間の両方で、属性または変数が動的に作成および削除されることも示しています。

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

変数を跳ね返すために使用されるので、このモジュールを「壁」と呼びます。これは、空のクラス「ルーター」のグローバル変数とクラス全体の属性を一時的に定義するスペースとして機能します。

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

このモジュールは、壁をインポートsourcefnし、メッセージを定義し、1つはグローバル経由で、もう1つはルーター機能経由で、2つの異なるメカニズムでメッセージを送信する単一の関数を定義します。変数wall.msgwall.router.messageは、それぞれの名前空間で初めてここで定義されることに注意してください。

dest.py

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

このモジュールはdestfn、2つの異なるメカニズムを使用して、ソースから送信されたメッセージを受信する関数を定義します。変数「msg」が存在しない可能性があります。destfn表示された変数も削除します。

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

このモジュールは、以前に定義された関数を順番に呼び出します。dest.destfn変数への最初の呼び出しの後、存在wall.msgwall.router.msgなくなりました。

プログラムからの出力は次のとおりです。

global:Hello world!
ルーター:こんにちは!
グローバル:メッセージ
ルーターなし:メッセージなし

上記のコードフラグメントは、モジュール/グローバルとクラス/クラス変数のメカニズムが本質的に同一であることを示しています。

多数の変数を共有する場合、名前空間の汚染は、wall1、wall2などの複数の壁タイプのモジュールを使用するか、1つのファイルで複数のルータータイプクラスを定義することによって管理できます。後者はやや整然としているため、クラス変数メカニズムを使用するためのわずかな利点になる可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.