質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

248閲覧

クラス変数を複数のモジュールで共有するには

T_Kamiya

総合スコア6

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2024/04/09 05:38

実現したいこと

あるクラスのクラス変数に、そのクラスが属しているモジュールからだけでなく他のモジュールからもアクセスしたい。

前提

クラス変数にはそのクラス内のメソッドを介して書き込み、読出しを行ない、クラスの外からは直接クラス変数にアクセスしないようにしています。
最終的にはクラス変数に__を付けて外部から隠蔽する予定です。

複数のモジュールで構成されているシステムで、どのモジュールからもクラス変数を読み書きできるようにするにはどうすればよいのでしょうか。特別な書き方があるのでしょうか。よろしくご教示ください。

発生している問題・エラーメッセージ

クラスが属しているモジュール(モジュール1)の中でだけクラス変数を読み書きしたときは、クラス変数が共有され書き換えた内容が保持されています。
しかしその書き換えたクラス変数を他のモジュール(モジュール2)から読み出すとクラス変数は書き換えられておらず定義した時の初期値のままになっています。
逆にモジュール2からクラス変数を書き換えた場合、モジュール2およびクラスが属するモジュール1のどちらでで読み出してもクラス変数が正しく書き換わっています。

とくにエラーメッセージは出ていません

該当のソースコード

Python

1以下の二つのモジュール作り、それぞれ実行した 2 3コード 4```# モジュール1(module_1.py クラスが属するモジュール) 5import module_2 as md2 6 7class Cls1: 8 s = ['A', 'B'] # クラス変数 9 10 def read_1(self): # クラス変数をmodule_1で読む 11 print(Cls1.s) 12 # print(self.__class__.s) 13 14 def wr_1(self, i, d): # クラス変数を書き換える 15 Cls1.s[i] = d 16 # self.__class__.s[i] = d 17 18if __name__ == '__main__': 19 c1 = Cls1() 20 21 c1.read_1() # module_1で読む:結果['A', 'B']初期値 22 md2.read_2() # module_2で読む:結果['A', 'B']初期値 23 24 c1.wr_1(0, 'a') # module_1でクラス変数を書き換え 25 c1.read_1() # module_1で読む:結果['a', 'B']書き換えOK 26 md2.read_2() # module_2で読む:結果['A', 'B']共有NG 27 28 29# モジュール2(module_2.py クラス変数とは別のモジュール) 30import module_1 as md1 31 32def read_2(): # クラス変数をmodule_2で読む 33 print(md1.Cls1.s) 34 # c1 = md1.Cls1() 35 # print(c1.__class__.s) 36 37if __name__ == '__main__': 38 c1 = md1.Cls1() 39 40 c1.read_1() # module_1で読む:結果['A', 'B']初期値 41 read_2() # module_2で読む:結果['A', 'B']初期値 42 43 c1.wr_1(1, 'b') # module_2からmodule_1のクラス変数を書換え 44 c1.read_1() # module_1で読む:結果['A', 'b']書換えOK 45 read_2() # module_2で読む:結果['A', 'b']共有OK 46

試したこと

上記のソースコードのモジュール1を実行すると、変数を書き換えた後に読み出したクラス変数がモジュール1とモジュール2で違う。モジュール1で読むとクラス変数は書き換わっているのに、モジュール2で読むと初期値のままである。

ところがモジュール2を実行してモジュール2からモジュール1のクラス変数を書き換えると、モジュール1、モジュール2のどちらで読んでもクラス変数はちゃんと書き換わっている。

上記のほかに以下のことを試しました。
A.クラスのメソッドをクラスメソッドに変えても結果は同じでした。
B.クラスのインスタンス経由でメソッドを呼んだときにメソッド内でコメントアウトしてあるように
self.class.s
の形式で書き換え、読出しを行なっても結果は同じでした。
C.全く別のモジュールにクラスを作り、その中にクラス変数だけを入れ、モジュール1とモジュール2からアクセスすると共有が正常に行われました。
ただこれではグローバル変数と同じで、クラス変数として置く意味がないように思います。

補足情報(FW/ツールのバージョンなど)

とくにありません### ヘディングのテキスト

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

pyhton module_1.py

と実行すると、まず module_1.py が __main__ という名前のモジュールとして読み込まれます
その中の import文の作用で module_2.py が module_2 という名前のモジュールとして読み込まれます
その中の import文の作用で module_1.py が module_1 という名前のモジュールとしてまた読み込まれます

つまり、これによって、__main__.Cls1module_1.Cls1 が、違うクラスとして存在することになります


一方で

pyhton module_2.py

と実行すると、まず module_2.py が __main__ という名前のモジュールとして読み込まれます
その中の import文の作用で module_1.py が module_1 という名前のモジュールとして読み込まれます
その中の import文の作用で module_2.py が module_2 という名前のモジュールとしてまた読み込まれますが一切使われません

これが挙動の差異の原因です

対処は循環参照を入れない、つまり実行するファイルを別の場所からimportするのを避けることです

投稿2024/04/09 08:48

編集2024/04/09 10:03
quickquip

総合スコア11040

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

T_Kamiya

2024/04/09 13:17

>module_1.py が __main__ という名前のモジュールとして読み込まれます このことは全く知りませんでした。てっきりmodule_1という名前のモジュールだと思っていました。 この結果として >__main__.Cls1 と module_1.Cls1 が、違うクラスとして存在する こともよくわかりました。 別のモジュールから実行するようにしたところクラス変数にmodulle_1、module_2のどちらからアクセスしても共有が問題なくできることが確認できました。 問題の原因が分かって解決でき、大変勉強になり感謝しています。 ありがとうございました。
quickquip

2024/04/09 14:22 編集

これ、「そのようには教わらない」だけで知らないということはないはずなのですよ。 if __name__ == '__main__': というイディオムだけ教えられていて、なぜこれが成立するのか? は置き去りにされているという話なのです。
guest

0

回答ではないのですが、なぜ、module_1.py で値が共有されないかについてです。

クラス定義そのものも、pythonのオブジェクトであり、クラス変数はそのオブジェクトに所属します。オブジェクトは読み込んだ場所(名前空間)によってことなるので、クラス定義が複数できる場合があります。

module_1と module_2 の適切な場所で「print( md1.Cls1) 」などとしてみると、どのようなオブジェクトが存在するかがわかるでしょう。

これが原因ですが、複数のモジュールから同じクラスオブジェクトにアクセスする方法についてはわかりません。(回答でない理由です)
そのようにしたいのが実用的な理由なのであれば、自分であればクラス変数以外のもっと確かな方法を採ります。

参考資料
https://docs.python.org/ja/3/tutorial/classes.html#classes

投稿2024/04/09 08:28

編集2024/04/09 08:29
TakaiY

総合スコア12774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

T_Kamiya

2024/04/09 13:17

>オブジェクトは読み込んだ場所(名前空間)によってことなるので、クラス定義が複数できる場合があります このことについてquickquipさんからもご指摘いただき、実行しようとするモジュールをmodule_1、module_2とは別に設けることでクラス定義が2つできることが回避でき問題が解決しました。 ご教示ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問