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

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

ただいまの
回答率

90.84%

  • Python 3.x

    4866questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 89

 やりたいこと

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

class Environment:
    def __init__(self, width, depth):
        self.Env = [[0 for w in range(width)] for d in range(depth)]
        self.updater = Updater(self)

    def getEnv(self):
        return self.Env

class Updater:
    def __init__(self, Environment):
        self.Env = Environment.Env

    def changeEnv(self, width, depth):
        self.Env = [[1 for w in range(width)] for d in range(depth)]


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

my_env = Environment(5, 5)
my_env.getEnv()
my_emv.updater.changeEnv(10, 10)
my_env.getEnv()

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

my_env.updater.Env

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

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

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

class Environment:
    def __init__(self, width, depth):
        self.Env = [[0 for w in range(width)] for d in range(depth)]
        self.updater = Updater(self)

    def getEnv(self):
        return self.Env

class Updater:
    def __init__(self, Environment):
        self.Env = Environment.Env

    def changeEnv(self, num):
        self.Env[0][0] = num


my_env = Environment(5, 5)
print(my_env.getEnv())
my_env.updater.changeEnv(10)
print(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に再代入してしまった場合』です。

class Environment:
    def __init__(self, width, depth):
        self.Env = [[0 for w in range(width)] for d in range(depth)]
        self.updater = Updater(self)

    def getEnv(self):
        return self.Env

class Updater:
    def __init__(self, Environment):
        self.Env = Environment.Env

    def swapEnv(self, new_env):
        self.Env = new_env


my_env = Environment(5, 5)
print(my_env.getEnv() is my_env.updater.Env)

my_env.updater.swapEnv([1, 2, 3])
print(my_env.getEnv())
print(my_env.updater.Env)

print(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インスタンスを直接保持するように

こういうことです。

class Environment:
    def __init__(self, width, depth):
        self.Env = [[0 for w in range(width)] for d in range(depth)]
        self.updater = Updater(self)

    def getEnv(self):
        return self.Env

class Updater:
    def __init__(self, Environment):
        self.Environment = Environment

    def changeEnv(self, width, depth):
        self.Environment.Env = [[1 for w in range(width)] for d in range(depth)]

my_env = Environment(5, 5)
print(my_env.getEnv())
my_env.updater.changeEnv(10, 10)
print(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 23:51

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

    キャンセル

  • 2018/05/11 23:56

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

    キャンセル

  • 2018/05/11 23:57 編集

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

    キャンセル

  • 2018/05/12 00:04

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

    キャンセル

  • 2018/05/12 00:16

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

    キャンセル

  • 2018/05/12 00:16

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

    キャンセル

  • 2018/05/12 00:17

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

    キャンセル

  • 2018/05/12 00:24 編集

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

    いえ、どちらの方法でも新規でオブジェクトが作られますよ。
    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]

    この挙動は理解できますか?

    キャンセル

  • 2018/05/12 00:35

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

    キャンセル

  • 2018/05/12 00: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

    註:空白をドットで代替しています。

    キャンセル

  • 2018/05/12 00:46

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

    キャンセル

  • 2018/05/12 00:47

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

    キャンセル

  • 2018/05/12 00:52

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

    キャンセル

  • 2018/05/12 00:59

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

    キャンセル

  • 2018/05/12 01:08

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

    キャンセル

  • 2018/05/12 01:13

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

    キャンセル

  • 2018/05/12 01:17

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

    キャンセル

  • 2018/05/12 01:23

    『新しいオブジェクトが作られる』というより、『参照値が書き換わる』がより適切ですかね。
    前者の場合コンストラクタが走るイメージがどうしてもあるので。

    A = B
    A = C したときは、A is B がFalseになりますが、

    A = B
    A.a = c したときは、A is B はTrueのままです。

    ---
    すべての道はなんとやら、そのとおりです。頑張ってくださいね。
    喜んで頂けて光栄です。

    キャンセル

  • 2018/05/12 01:25

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

    キャンセル

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

  • ただいまの回答率 90.84%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    python3とPyQt5を使って、対称図形を出力したい

    前提・実現したいこと python3とPyQt5を使って、以下の画像のような対称図形を出力したいが、うまくいきません。よろしくお願いします。  y軸の変換に問題があるようなので

  • 解決済

    (Python)構文間違い

    構文間違い(SyntaxError: invalid syntax)と怒られましたけど、 どこが間違っているのかを教えてください。 コード: class michael:

  • 解決済

    AttributeError: 'Cifar10Reader' object has no attr...

    AttributeError: 'Cifar10Reader' object has no attribute 'bytestream'  のエラー が出ました。 reader.

  • 解決済

    Python メソッド間の変数の呼び出しについて

    Pythonを勉強しておりまして、現在は主にオブジェクト指向の考え方を学んでおります(オブジェクト指向はほとんど初学です)。これまでに書いたシンプルなプログラムをクラスとメソッドで

  • 解決済

    Python3でのP制御の発散について

    前提・実現したいこと Python3を用いて制御工学のP制御〜PID制御を学びたいと思い書きました。 目標は自由落下の物体に対して下から加速度を与え、100m付近で安定させることで

  • 解決済

    Pythonのループ操作

    Python超初心者です。 以下のような計算を行いたいときにはどのようなコードを書けばいいでしょうか? ある原子の座標があったとして、それらのステップ毎の計算を一気に行いたいで

  • 解決済

    python3 クラスを呼び出すとクラス内の処理を全て実行する

    クラスを呼び出すと自動で、クラス内の処理[関数1,関数2]を実行したいと思います。 class Hello(): @classmethod def Sain(

  • 解決済

    python3 簡易人生ゲーム 設計について

    乱数で進んだ。止まったマスに書かれた文字を取得したい。 移動した数値を格納したのですが、文字を取得する方法が思い付きません。 取得する文字の重複はなしです。取得文字をdel関数

同じタグがついた質問を見る

  • Python 3.x

    4866questions

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