何が表示されるでしょうか?
次のPythonは実行すると何が表示されるだろうか。
緑三角ボタンをクリックし実行してください。
予想通りではないだろうか。変数xにABCを保存しyにxを保存。この時点でxとyは同じ。その後xにDEFを保存。これでx,yは違った内容となる。
では次のプログラムはどうだろうか。
緑三角ボタンをクリックし実行してください。
変数xにリストを保存しyにxを保存。この時点でxとyは同じ。その後xの0番目をzに変更。これでx,yは違った内容だが・・・。同じだ。
この理由を知るにはメモリーを意識しなければいけない。
なぜ変更されるのか
メモリーは名前の通りデータを記憶するモノである。記憶場所にはアドレス(番地)が割り当てられており、アドレスを目印に値を記憶させることになる。
高級言語では直接アドレスを操作できない。しかしPythonではid関数で、利用しているアドレスがわかる。先程のxとその中身x[0]、x[1]、x[2]、を表示する。
緑三角ボタンをクリックし実行してください。
xのアドレスと各データのアドレスが違っている。模式化すると次の通りだ。
xにはリストのアドレスだ。リスト構造は複雑なので各値は独立したメモリに保存され紐づけられている。
ではここでyにxを代入するとどうなるのか。yのアドレスを確認する。
緑三角ボタンをクリックし実行してください。
yはxと同じアドレスである。そのイメージだ。
さきほどこの状態でxの0番目をZに変更した。下記の状態だ。
0番目の値は別アドレスになり値が変更される。しかしリスト情報のアドレスに変化はない。これはxとyは、同じ値を参照していることになる。そのためxの値を変更するとyも変更されるというわけだ。
なぜ変更されないのか
リストとは違い、文字列yは変更されなかった。このリストと文字列の違いは、メモリー構造で理解できる。
リストと違い文字列はシンプルな構造だ。ここでxの値を変更すると、
xが変更値のアドレスになる。yは前のアドレスのままなので、xの値が変更されようともyには影響がない。
リストを別の変数に入れる方法
このようにメモリー構造を意識しないと思わぬ動きになる。ではリストを独立した他の変数にコピーするにはどうしたらいいだろうか。
名前の通り、copy命令で実現できる。
緑三角ボタンをクリックし実行してください。
3行目のcopyでxをコピーしyに保存している。その後にアドレスを表示しているがx,yは違うアドレスになっている。結果、yは変更されず、xとは別の変数だと確認できる。
これで解決したように思えるが、ところが・・・
緑三角ボタンをクリックし実行してください。
これは二次元配列だ。copy命令によりx,yは別のアドレスになっている。しかし、x[0][0]をAAに変更すると、yにも反映されている。なぜだろうか?メモリー構造で確認する。
copy命令によりリスト情報は2つになりx,yは別アドレスだ。しかし二次元配列はさらにリスト情報がある。これはcopyではコピーされない。このためxの値を変更するとyも変更されるのだ。
確認のため1つ目の配列アドレスを表示してみると同じことがわかる。
緑三角ボタンをクリックし実行してください。
まったく面倒だなぁーと感じている人も多いと思う。これで最後だ。なにもかもコピーする最強のdeepcopyだ。これは標準モジュールのcopyに入っているのでimportで呼び出し使う。
緑三角ボタンをクリックし実行してください。
xを変更してもyは影響をうけていない。これで完了!。
メモリーを意識する
プログラム学習始めたばかりの人がこの記事を読むと、ややこしく苦痛に感じるかもしれない。この内容は一旦忘れてしまい、プログラムを思い通りに作成できる、を目指すのが良い。そしてその後に、このメモリーを意識することを思い出して欲しい。
プログラミング経験のある人なら、このようにメモリー(データ構造等)を意識すると他にも役立つことがある。例えば、newで何が起こるなどだ。ぜひメモリーを意識しプログラミング作成を行って欲しい。
つづくかもしれない