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

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

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

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

Q&A

解決済

1回答

1153閲覧

あるクラスに属する変数を、他の異なるクラスからも変更したい

anytimeanywhere

総合スコア11

Python 3.x

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

0グッド

0クリップ

投稿2018/05/11 14:26

編集2018/05/11 15:02

やりたいこと

以下の二つのクラスがあったとします

python3

1class Environment: 2 def __init__(self, width, depth): 3 self.Env = [[0 for w in range(width)] for d in range(depth)] 4 self.updater = Updater(self) 5 6 def getEnv(self): 7 return self.Env 8 9class Updater: 10 def __init__(self, Environment): 11 self.Env = Environment.Env 12 13 def changeEnv(self, width, depth): 14 self.Env = [[1 for w in range(width)] for d in range(depth)]

このとき、二次元配列EnvをEnvironmentにてただひとつだけ用意し、Updaterなどの別クラスからアクセス、変更可能にしたいのですが、実際は

python3

1my_env = Environment(5, 5) 2my_env.getEnv() 3my_emv.updater.changeEnv(10, 10) 4my_env.getEnv()

としても1回目のgetEnv()と2回目のgetEev()は同じ結果を返し、変更が反映されません。
このとき

python3

1my_env.updater.Env

では変更が反映されているのですが、もうこのEnvとEnvironmentのEnvは別物になってしまっています。

本当はここで新たなEnvが生まれるのではなく、EnvironmentのEnvが変更されるようにしたいのです。

どうすれば良いでしょうか?
皆様この初心者にご教授くださいませ
よろしくお願い致します

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

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

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

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

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

guest

回答1

0

ベストアンサー

1回目のgetEnv()と2回目のgetEev()は同じ結果を返し、変更が反映されません。

いえ、変更が反映されますけれど... ところどころ小さなバグは直してます。

Python

1class Environment: 2 def __init__(self, width, depth): 3 self.Env = [[0 for w in range(width)] for d in range(depth)] 4 self.updater = Updater(self) 5 6 def getEnv(self): 7 return self.Env 8 9class Updater: 10 def __init__(self, Environment): 11 self.Env = Environment.Env 12 13 def changeEnv(self, num): 14 self.Env[0][0] = num 15 16 17my_env = Environment(5, 5) 18print(my_env.getEnv()) 19my_env.updater.changeEnv(10) 20print(my_env.getEnv())

実行結果 Wandbox

[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] [[10, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

ひょっとしたら

ハマるとすれば、『Envに再代入してしまった場合』です。

Python

1class Environment: 2 def __init__(self, width, depth): 3 self.Env = [[0 for w in range(width)] for d in range(depth)] 4 self.updater = Updater(self) 5 6 def getEnv(self): 7 return self.Env 8 9class Updater: 10 def __init__(self, Environment): 11 self.Env = Environment.Env 12 13 def swapEnv(self, new_env): 14 self.Env = new_env 15 16 17my_env = Environment(5, 5) 18print(my_env.getEnv() is my_env.updater.Env) 19 20my_env.updater.swapEnv([1, 2, 3]) 21print(my_env.getEnv()) 22print(my_env.updater.Env) 23 24print(my_env.getEnv() is my_env.updater.Env)

実行結果 Wandbox

True [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] [1, 2, 3] False

こっちでしょうか?

質問編集を受けて

UpdaterインスタンスがEnvironmentインスタンスを直接保持するように

こういうことです。

Python

1class Environment: 2 def __init__(self, width, depth): 3 self.Env = [[0 for w in range(width)] for d in range(depth)] 4 self.updater = Updater(self) 5 6 def getEnv(self): 7 return self.Env 8 9class Updater: 10 def __init__(self, Environment): 11 self.Environment = Environment 12 13 def changeEnv(self, width, depth): 14 self.Environment.Env = [[1 for w in range(width)] for d in range(depth)] 15 16my_env = Environment(5, 5) 17print(my_env.getEnv()) 18my_env.updater.changeEnv(10, 10) 19print(my_env.getEnv())

実行結果 Wandbox

[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

EnvironmentクラスがEnv属性を持ってしまっているのが問題をややこしくしているような。

投稿2018/05/11 14:33

編集2018/05/11 15:09
LouiS0616

総合スコア35660

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

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

anytimeanywhere

2018/05/11 14:51

ごめんなさい、スマホから問題を思い出しながら中途半端に質問してしまったため、間違いがありました。 LouiS0616様が訂正してくださった通り Updaterのなかで def changeEnv(): self.Env = #たとえば全要素を1にする のように再代入していることによる問題です。
anytimeanywhere

2018/05/11 14:56

どうすればUpdaterから自由にEnvironmentのEnvを変更できるでしょうか? 前のEnvを捨てて新しいものをEnvに設定し直す def setEnv(env): self.Env = env をEnvironmentにて定義し、それをUpdaterから呼び出す形で変更するべきでしょうか?
LouiS0616

2018/05/11 14:58 編集

それが一番素直な方法かと思います。 あるいは、UpdaterインスタンスがEnvironmentインスタンスを直接保持するようにすれば良いです。
anytimeanywhere

2018/05/11 15:04

回答ありがとうございます。 もしよろしければ二つ目の方法についてもう少し詳しく教えてくださいませんか? 直接の意味がわかっておりません
anytimeanywhere

2018/05/11 15:16

self.Env = Environment.Env (別インスタンスから変数の取得) としてのself.Envへの再代入では、新しいオブジェクトになってしまうが、self.Environment = Environment (別インスタンスそのものの取得) としself.Environment.Env (別インスタンスの変数; not このインスタンスの、もとは別インスタンスから取得した変数)への代入では新しいオブジェクトは作られない
anytimeanywhere

2018/05/11 15:16

という理解で間違いないでしょうか?
anytimeanywhere

2018/05/11 15:17

確かにこの属性の命名は今後拡張時にも混乱を招きそうです...
LouiS0616

2018/05/11 15:25 編集

> という理解で間違いないでしょうか? いえ、どちらの方法でも新規でオブジェクトが作られますよ。 Pythonの変数が保持しているのは『参照値』と呼ばれる値で、これが書き換えられるかどうかで元のデータへの影響が異なります。 l1 = [1, 2, 3] l2 = l1 l2[0] = 10 print(l1, l2) # [10, 2, 3] [10, 2, 3] l2 = [0] print(l1, l2) # [10, 2, 3] [0] この挙動は理解できますか?
anytimeanywhere

2018/05/11 15:35

はい(完璧な理解ではないと思いますが、挙動に不信は抱きません) 上のようなindex指定での代入は、indexがl1のものなので、l1,l2ともに変更され、下のような再代入では、l2だけが変更されます
LouiS0616

2018/05/11 15:41

それでは、こちらはどうでしょう。似たようなコードですが。 class Spam: ...def __init__(self): ......self.val = 42 spam = Spam() spam.val = 10 print(spam.val) # 10 v = spam.val v = -1 print(spam.val) # 10 註:空白をドットで代替しています。
anytimeanywhere

2018/05/11 15:46

これはvに再代入したパターンではないでしょうか print(v) #-1
LouiS0616

2018/05/11 15:47

そうですね。 Environment.Envへの代入と、 Env = Environment.Env をしてから Envへ代入することはこれくらいの違いがあるということです。
anytimeanywhere

2018/05/11 15:52

大変よくわかりました。 遅くまでお付き合いいただきありがとうございました。 最後にひとつだけ質問があるのですが、 なぜEnv = Environment.Envをしたとき再代入によって新しいオブジェクトが作られる必要があるのでしょうか?
LouiS0616

2018/05/11 15:59

あれ、ひょっとしたら何か齟齬があったかもしれません。 Env = Environment.Envをしたときに新しいオブジェクトは作られないです。
LouiS0616

2018/05/11 16:08

ここらへんの話はC言語を勉強しておくと腑に落ちやすいです。 いわゆる『ポインタのポインタ』に関連する話です。
anytimeanywhere

2018/05/11 16:13

Env = Eevironment.Env ののち Env = 5 などとしたときに Env is Environment.Env がFalseになるということでした
anytimeanywhere

2018/05/11 16:17

すべての道はなんとやらということですか。 素晴らしい回答者様のおかげで一歩進めました。 C言語を通じて言語設計の妙についても学んで行けたらいいなと思います。 今回は本当にありがとうございました。
LouiS0616

2018/05/11 16:23

『新しいオブジェクトが作られる』というより、『参照値が書き換わる』がより適切ですかね。 前者の場合コンストラクタが走るイメージがどうしてもあるので。 A = B A = C したときは、A is B がFalseになりますが、 A = B A.a = c したときは、A is B はTrueのままです。 --- すべての道はなんとやら、そのとおりです。頑張ってくださいね。 喜んで頂けて光栄です。
anytimeanywhere

2018/05/11 16:25

最後まで常に分かり易くしてくださり理解することができました。 回答者様の経験と能力の高さに脱帽です。 頑張ります! ありがとうございました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問