web-dev-qa-db-ja.com

インポートされたモジュールのグローバル変数の可視性

Pythonスクリプトでモジュールをインポートしている壁に少し遭遇しました。エラー、エラーに遭遇した理由、問題を解決するためにこの特定のアプローチを結び付けている理由を説明するために最善を尽くします(すぐに説明します)。

いくつかのユーティリティ関数/クラスを定義したモジュールがあり、この補助モジュールがインポートされるネームスペースで定義されたエンティティを参照するとします(「a」をそのようなエンティティとします)。

module1:

def f():
    print a

そして、私はこれらのユーティリティをインポートしたい「a」が定義されているメインプログラムを持っています:

import module1
a=3
module1.f()

プログラムを実行すると、次のエラーがトリガーされます。

Traceback (most recent call last):
  File "Z:\Python\main.py", line 10, in <module>
    module1.f()
  File "Z:\Python\module1.py", line 3, in f
    print a
NameError: global name 'a' is not defined

同様の質問がされました 過去(2日前、d'uh)といくつかの解決策が提案されましたが、これらが私の要件に合うとは思いません。 ここに私の特定のコンテキストがあります:

MySQLデータベースサーバーに接続し、GUIでデータを表示/変更するPythonプログラムを作成しようとしています。簡潔にするために、MySQL関連の一連の補助/ユーティリティ関数を別のファイルに定義しました。しかし、それらはすべて共通の変数を持っています。私はもともとユーティリティモジュールでinsideを定義し、MySQLdbのcursorオブジェクトですモジュール。後で、cursorオブジェクト(dbサーバーとの通信に使用)をメインモジュールで定義する必要があることに気付きました。メインモジュールとそこにインポートされたものはすべて、そのオブジェクトにアクセスできます。

最終結果は次のようになります。

utilities_module.py:

def utility_1(args):
    code which references a variable named "cur"
def utility_n(args):
    etcetera

そして私のメインモジュール:

program.py:

import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

そして、ユーティリティ関数のいずれかを呼び出そうとすると、前述の「グローバル名が定義されていません」というエラーが発生します。

特定の提案は、ユーティリティファイルに次のような「from program import cur」ステートメントを含めることでした。

utilities_module.py:

from program import cur
#rest of function definitions

program.py:

import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

しかし、それは周期的なインポートまたはそのようなものであり、最終的にはクラッシュします。だから私の質問は:

メインモジュールで定義された「cur」オブジェクトを、インポートされた補助関数から見えるようにするにはどうすればよいですか?

解決策が他の場所に投稿されている場合、お時間をいただき、深くおdeepび申し上げます。私は自分で答えを見つけることができず、私の本にはこれ以上のトリックはありません。

82
Nubarke

Pythonのグローバルは、グローバルモジュールに対してであり、すべてのモジュールにわたってではありません。 (たとえば、Cでは、明示的にstaticにしない限り、グローバルはすべての実装ファイルで同じであるため、多くの人がこれに混乱しています。)

実際のユースケースに応じて、これを解決するさまざまな方法があります。


この道を進む前に、これが本当にグローバルである必要があるかどうかを自問してください。たぶん、単に無料の関数ではなく、インスタンスメソッドとしてfを持つクラスが必要ですか?次に、このようなことをすることができます:

import module1
thingy1 = module1.Thingy(a=3)
thingy1.f()

本当にグローバルが必要なのに、module1で使用するためだけにある場合は、そのモジュールで設定してください。

import module1
module1.a=3
module1.f()

一方、aが多くのモジュールで共有されている場合は、別の場所に配置し、全員にインポートしてもらいます。

import shared_stuff
import module1
shared_stuff.a = 3
module1.f()

…そして、module1.pyで:

import shared_stuff
def f():
    print shared_stuff.a

しない変数が定数であることが意図されていない限り、fromインポートを使用します。 from shared_stuff import aは、インポート時に参照されるshared_stuff.aに初期化された新しいa変数を作成し、この新しいa変数はshared_stuff.aへの割り当ての影響を受けません。


または、組み込みのようにどこでも真にグローバルであることが本当に必要なまれなケースでは、組み込みモジュールに追加します。正確な詳細は、Python 2.xと3.xで異なります。 3.xでは、次のように機能します。

import builtins
import module1
builtins.a = 3
module1.f()
197
abarnert

回避策として、このように外部層で環境変数を設定することを検討できます。

main.py:

import os
os.environ['MYVAL'] = str(myintvariable)

mymodule.py:

import os
print os.environ['MYVAL']

追加の予防策として、MYVALがモジュール内で定義されていない場合に対処してください。

5
Vishal

関数はdefined inであるモジュールのグローバルを使用します。たとえば、a = 3を設定する代わりに、module1.a = 3を設定する必要があります。したがって、curutilities_moduleでグローバルとして使用できるようにするには、utilities_module.curを設定します。

より良い解決策:グローバルを使用しないでください。必要な変数を必要な関数に渡すか、クラスを作成してすべてのデータをまとめて、インスタンスの初期化時に渡します。

4
kindall

この投稿は、私が遭遇したPython動作の単なる観察です。もしあなたが私が下でやったのと同じことをしたなら、あなたが上で読んだアドバイスはあなたには役に立たないでしょう。

つまり、グローバル/共有変数を含むモジュールがあります(上記で提案したとおり)。

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

次に、共有のものをインポートするメインモジュールがありました:

import sharedstuff as shared

これらの配列に実際に配置された他のモジュール。これらはメインモジュールによって呼び出されます。これらの他のモジュールを終了すると、配列が読み込まれていることがはっきりとわかります。しかし、メインモジュールでそれらを読み返すとき、それらは空でした。これは私にとってかなり奇妙でした(私はPythonが初めてです)。ただし、メインモジュールでsharedstuff.pyをインポートする方法を変更すると、次のようになります。

from globals import *

うまくいきました(アレイにデータが入力されました)。

言ってるだけ'

4
user3336548

この特定の問題に対する最も簡単な解決策は、モジュール内の変数グローバルにカーソルを保存する別の関数をモジュール内に追加することでした。その後、他のすべての機能も同様に使用できます。

module1:

cursor = None

def setCursor(cur):
    global cursor
    cursor = cur

def method(some, args):
    global cursor
    do_stuff(cursor, some, args)

メインプログラム:

import module1

cursor = get_a_cursor()
module1.setCursor(cursor)
module1.method()
2
Chris Nasr

グローバルはモジュール固有であるため、インポートされたすべてのモジュールに次の関数を追加し、それを使用して次のことができます。

  • それらのグローバルとして特異変数(辞書形式)を追加する
  • mainモジュールグローバルをそこに転送します。

addglobals = lambda x:globals()。update(x)

次に、現在のグローバルを渡す必要があるのは次のとおりです。

インポートモジュール

module.addglobals(globals())

1
user2589273

OOPこれを行う方法は、モジュールをバインドされていないメソッドのセットではなくクラスにすることです。次に、__init__またはセッターメソッドを使用して、モジュールメソッドで使用する呼び出し元から変数を設定できます。

0
coyot

上記の回答では見ていなかったので、呼び出しモジュールのグローバルを必要とする関数にglobal_dict引数を追加してから関数にdictを渡すという単純な回避策を追加すると思いました呼び出すとき;例えば:

# external_module
def imported_function(global_dict=None):
    print(global_dict["a"])


# calling_module
a = 12
from external_module import imported_function
imported_function(global_dict=globals())

>>> 12
0
Toby Petty