[[0]*m]*nの注意点

 最近、C言語で書いていたMASのコードをpythonに書き直してみようかといろいろ画策しております。ですが、C言語でできたことがpythonでもできるわけではなかったりするので注意が必要です。私の場合、得てして、ベクトルや行列を扱いたいわけで、その代替品がリストや配列です。

  • リストと配列は違う

pythonには配列が存在しないので、リストを配列のように使うことが多いと思います。numpyのarrayを使う人はそちらのほうがよいと思いますが、僕はあいにく使っていないので、現時点では生のpythonでやることにします。
さて、次のような0を含む配列の配列に、一定数の個数1を代入したいとすれば、僕はC言語で次のように記述します。

#include<stdio.h>
#define N  10
#define Ns  3
int main(){
  int i,j,k=0;
  int a[N][N];
  for(i=0;i<N;i++) for(j=0;j<N;j++) a[i][j]=0;
  while(k<Ns){
    i=rand()%N;
    j=rand()%N;
    if(a[i][j] == 0) {
      k++;
      a[i][j] = 1;
    }
  }
  for(i=0;i<N;i++) {for(j=0;j<N;j++) printf("%d ",a[i][j]);printf("\n");}
}

そうすると、次のように行列を表示してくれます。

$ gcc -o array array.c
$ ./array.exe
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
1 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 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 0 0 0 1 0 0 0 0 0 

pythonで同じことをしようとしたところ、初期値0の入力で、気を抜くと変なことが起こる場合があるので注意が必要です。

>>> a = [[0]*3]*3#ここがだめ
>>> a
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[0][0]
0
>>> a[0][0]=1
>>> a
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]

 [[0]*m]*nって別段普通に使えると思ってたのですが。いざ代入しようと思った時に僕としては、一か所だけ変更したくても、つられて一列変わっちゃうわけで。これは、使いにくい。折角短くかけるのに、どうしてこうなるの~。
ちゃんとやろうと思ったら、

>>> a = [[0 for i in range(3)] for j in range(3)]
>>> a
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[0][0] = 1
>>> a
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

これならちゃんと、一か所だけ変わってくれます。なんで揃ってないのだろうか。うーん、"zen of python"なんでしょうきっと。
まぁ、ともあれ、ちゃんと意図した動きをしてくれるコードは以下のようになりました。

import random

Lx, Ly = 3, 3
a  = [[0 for i in range(Lx)] for j in range(Ly)]

k  = 0
Ns = 2
while k < Ns:
    i, j = random.randrange(Lx), random.randrange(Ly) 
    if a[i][j] == 0:
        k += 1
        a[i][j] = 1
[print(i) for i in a]

インデックスkを必要としない書き方ができるならそうしたいものです。
おまけにクラスを使っても書いてみよう。

class Matrix:
    def __init__(self, raw, col):
        self.Lx = raw
        self.Ly = col
        self.m = [[0 for i in range(raw)] for j in range(col)]

    def show(self):
        [print(i) for i in self.m]

    def puts(self, sup):
        import random
        k = 0
        while k < sup:
            i, j = random.randrange(self.Lx), random.randrange(self.Ly)
            if self.m[i][j] == 0:
                k += 1
                self.m[i][j] = 1

if __name__ == "__main__":
    a = Matrix(3,3)
    a.puts(3)
    a.show()