3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python のリストでは=の使用注意

Last updated at Posted at 2017-11-19

確認したバージョンはPython 3.6.1

それは.extend()を使ったとき起こった。
#基本

#3つリスト作って
a = ["dog", "cat", "rabbit"]
b = list("333")#['3', '3', '3']
c = [2, "toka", 3.4, "みたいな混在もOK"]
d = [a,b]
print(d)

ここまでなら、間違いなく[['dog', 'cat', 'rabbit'], ['3', '3', '3']]が表示される。
そしてリストを追加する関数として.extend()があり、これでcを追加しようとした。

#問題のコード

a = ["dog", "cat", "rabbit"]
b = list("333")#['3', '3', '3']
c = [2, "toka", 3.4, "みたいな混在もOK"]
d = [a,b]

#dと比較するためにeにdを代入して
e = d

#.exrend()でeにcを挿入
e.extend(c)

#では比較を
print("d=", d)
print("e=", e)

結果表示されたのは

d= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']
e= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']

あれ〜?

方法を変えてみよう

a = ["dog", "cat", "rabbit"]
b = list("333")#['3', '3', '3']
c = [2, "toka", 3.4, "みたいな混在もOK"]
d = [a,b]

ここまではいじらずに、処理を変えてみる

e = d

#.extend()と同じ結果が+=でもできる。
e += c

print("d=", d, "\n", "e=", e)

"""結果
d= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']
e= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']
"""

失敗。

そもそも、いじってないはずのdが変化してるのだ。
しかも、加えるのをeではなくdに変えても結果は同じ。
原因はほぼ確実に e = d という処理。
うまくいくわけ無いか。
なら

e = d + c

print("d=", d, "\n", "e=", e)

"""結果
d= [['dog', 'cat', 'rabbit'], ['3', '3', '3']]
e= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']
"""

成功。
やはり、e = d が問題のようだ。
しかし、なんでこんなことに?

#原因
どうも、e = dで行われたのはeにdのリストをコピーではなく、eとdという変数のIDを共通化という処理がなされたよう。

a = ["dog", "cat", "rabbit"]
b = list("333")#['3', '3', '3']
c = [2, "toka", 3.4, "みたいな混在もOK"]
d = [a,b]
e = d

#id()で変数のidを確認。
print("d=", id(d), "\ne=", id(e))

e.extend(c)

print("d=", d)
print("e=", e)
print("d=", id(d), "\ne=", id(e))
"""
結果
d= 4738395144 
e= 4738395144
d= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']
e= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']
d= 4738395144 
e= 4738395144
"""

つまり、eとタグ付けされた箱にdとタグ付けされた箱と同じ物を入れたのではなく、eというタグをdとタグ付けされた箱に貼っただけと。
だから、eへの処理はdへの処理と同じ意味を持ったと。
何じゃそりゃ。

#解決策
未だ初心者のため詳しい仕組みはわからない。
とりあえず成功例から考えて、何かの処理を加えて単なるコピーじゃなくしたらいけるかと、スライスを使ってコピーすることに

a = ["dog", "cat", "rabbit"]
b = list("333")#['3', '3', '3']
c = [2, "toka", 3.4, "みたいな混在もOK"]
d = [a,b]

#e = dをe = d[:]に変更
e = d[:]

e.extend(c)

print("d=", d, "\n", "e=", e)
print("d=", id(d), "\n", "e=", id(e))


"""結果
d= [['dog', 'cat', 'rabbit'], ['3', '3', '3']]
e= [['dog', 'cat', 'rabbit'], ['3', '3', '3'], 2, 'toka', 3.4, 'みたいな混在もOK']
d= 4738473480 
e= 4739188744
"""

とりあえず成功。
これが何らかのバグなのか、それとも仕様なのかは不明。
こういう仕様のようです。

他の方法としては、

e = d
e = e[:]
e = list(d)

でもdとeのIDは別になった。

#因みに

これは果たして、リストだけに起こる問題なのか?
と思い下のコードを実行

a = 1
b = 2
c = b
print("ID(a)=", id(a), "\nID(b)=", id(b), "\nID(c)=", id(c))

b += a
print(a,b,c)
print("ID(a)=", id(a), "\nID(b)=", id(b), "\nID(c)=", id(c))

"""結果
ID(a)= 4506339328 
ID(b)= 4506339360 
ID(c)= 4506339360
1 3 2
ID(a)= 4506339328 
ID(b)= 4506339392 
ID(c)= 4506339360
"""

こっちでは、c = bの時点ではIDはおなじだったが、b += aの処理でbのIDが変更された。
その後、a,bをリストにして試したら上の問題が発生。
少なくとも普通の計算では起こらない模様。

安心するべき・・・か?
むしろ、この単純計算だからこそこういった結果になっただけで、オブジェクト(変数)の中にオブジェクト(変数)が入る処理なら、同様の問題が起こるもよう。(set()とか、dict()でも確認)

#更に、因みに

問題のコードで
e = d のままにして
e.extend(c)の処理の代わりに、

  • e.append()やってみた。
    • 同じ問題が起きた。
  • e[1]=[9]にした
    • 同じ問題が起きた。
  • e.remove(['3', '3', '3'])してみた
    • 同じ問題が起きた。
  • e = [0]にした。
    • eのID変わった。

どうやら、元の形式をベースに変更する限りIDは引き継がれる模様。

3
1
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?