54352


Pythonで隣接行列を表現しようとしたら２次元配列が期待と異なる挙動をしてしまったため、どういう理由でこういう挙動になったのか？どうするのがよいのか？の二点を質問させてください。

### やりたいこと

無向グラフを隣接行列で表現したい。  
リンクが有るところは1で、ないところは0にしたい。  
最初にノードの数がわかるので、0で２次元配列Aを初期化したい。  
初期化の方法で下記のAのような方法をとった。

### 挙動

[code]

    A = [[0] * 3] * 3
    # A = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    
    試しに1つだけリンクを追加してみる
    A[0][1] = 1
    # [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
    
    
[/code]

### 期待する動作

[code]

    A[0][1] = 1としたら、
    [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
    となると期待
    
[/code]

### (ちなみに)

[code]

    B = [[0,0,0],[0,0,0],[0,0,0]]
    として同じことをやると
    # [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
    となってくれて期待通りに動いてくれます。
    が、これだとノードの数がわかっても動的に隣接行列を初期化できないためこの方法はとりたくない。
    
[/code]

よろしくおねがいします。


CC BY-SA 4.0
2019-04-21T00:32:01.693
54350
2019-04-21T06:24:34.303
32428
question
python array
Pythonの配列の作り方の違いによる挙動の変化について


`A = [[0] * 3] * 3`と言うのは、ほぼ次のようなコードと等価だと言えば少し理解しやすくなるでしょうか。

[code]

    c = 0
    b = [c,c,c]
    A = [b,b,b]
    
[/code]

Pythonのリストは参照で保持されます。`* 3`という演算ではその参照がコピーされるだけなので、

`A = [[0] * 3] * 3`という式では、内側のリスト`[0, 0, 0]`は一つだけ作られて、`A[0]`, `A[1]`,
`A[2]`のどれもが同じリストを参照することになるのです。

例えばこんな書き方をすると、お望みの動作になると思います。

[code]

    >>> A = [[0 for j in range(3)] for i in range(3)]
    >>> A
    [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    >>> A[0][1] = 1
    >>> A
    [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
    
[/code]

内側の方は、`*`を使って、`A = [[0]*3 for i in
range(3)]`でも良い(`*`が評価されるたびに新しいリストを作るため)んですが、少し分かりにくいかもしれません。


CC BY-SA 4.0
2019-04-21T01:38:17.133
54352
2019-04-21T02:10:01.313
2019-04-21T02:10:01.313
19110
13972
54350
answer


以下のようにfor文等でappendを必要な回数繰り返されたらどうでしょうか？

※すみません、pythonらしい書き方を知りません。  
※提示されているAとBの様子（構造）の違いを表示させたかったのですが、当然、typeではどうにもならず。でした。

追記：  
以下のようにid()関数で、確認できるようです。  
A[0]とA[1]は、同じものである、idが同じであることがわかります。  
イミュータブル等の説明で、こうなることは、一部、納得できる。

>>> A=[[0]*3]*3  
>>> A  
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]  
>>> id(A[0])  
1639979327816  
>>> id(A[1])  
1639979327816  
>>>

参考：  
<https://qiita.com/utgwkk/items/5ad2527f19150ae33322>

※元の現象は、listの__mul__()のコードを見れば理解できるのかもしれません。

[code]

    >>> out_a = []
    >>> req_n = 5
    >>> for x in range(0,req_n-1):
    ...   out_a.append([0,0,0])
    ...
    >>> out_a
    [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
    >>> out_a[0][1] = 1
    >>> out_a
    [[0, 1, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
    >>>
    
[/code]


CC BY-SA 4.0
2019-04-21T02:08:54.613
54353
2019-04-21T06:24:34.303
2019-04-21T06:24:34.303
31548
31548
54350
answer