重複の可能性あり:
+= がリスト上で予期しない動作をするのはなぜですか?
今日、Python 言語の興味深い「機能」を発見しましたが、とても残念でした。
>>> a = [1, 2, 3]
>>> b = "lol"
>>> a = a + b
TypeError: can only concatenate list (not "str") to list
>>> a += b
>>> a
[1, 2, 3, 'l', 'o', 'l']
どうですか?この2つは同等だと思っていたのに!さらに悪いことに、これはデバッグに非常に苦労したコードです
>>> a = [1, 2, 3]
>>> b = {'omg': 'noob', 'wtf' : 'bbq'}
>>> a = a + b
TypeError: can only concatenate list (not "dict") to list
>>> a += b
>>> a
[1, 2, 3, 'omg', 'wtf']
何てことだ!コード内にリストと辞書があり、.keys() を呼び出さずに辞書のキーをリストに追加してしまうなんて、一体どういうことなのかと不思議に思っていました。結局、こうなりました。
2 つのステートメントは同等のものであると考えていました。それを無視しても、文字列をリストに追加する方法は理解できます (文字列は単なる文字配列であるため) が、辞書の場合はどうでしょうか。(キー、値) タプルのリストを追加する場合は可能かもしれませんが、リストに追加するキーのみを取得するのは完全に恣意的に思えます。
この背後にあるロジックを知っている人はいますか?
ベストアンサー1
これは、一般的には可変性の問題であり、具体的には演算子のオーバーロードの問題です。C++ でも同様です。
式は、とa + b
にバインドされているオブジェクトから新しいリストを計算しますが、これらのオブジェクトは変更されていません。これを に代入し直すと、1 つの変数のバインドが新しい値を指すように変更されます。 は対称であることが予想されるため、辞書とリストを追加することはできません。a
b
a
+
この文はa += b
、 にバインドされている既存のリストを変更しますa
。オブジェクトの ID は変更されないため、変更は で表されるオブジェクトへのすべてのバインドに表示されますa
。演算子は+=
明らかに対称的ではなく、2 番目のオペランドを反復処理する と同等ですlist.extend
。辞書の場合、これはキーをリストすることを意味します。
議論:
オブジェクトが を実装していない場合、Pythonはと を+=
使ってそれを同等の文に変換します。つまり、2つは次のようになります。+
=
時には同等である、関係するオブジェクトの種類によって異なります。
+=
参照先を変更する (参照であるオペランド値ではなく)の利点は、実装の複雑さがそれに応じて増加することなく、実装をより効率的にできることです。
他の言語では、より明白な表記法を使用する場合があります。たとえば、演算子のオーバーロードがない架空のバージョンの Python では、次のようになります。
a = concat(a, b)
対
a.extend(a, b)
演算子表記は、実際にはこれらを省略した表現です。
ボーナス:
他の反復可能オブジェクトでも試してください。
>>> a = [1,2,3]
>>> b = "abc"
>>> a + b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "str") to list
>>> a += b
>>> a
[1, 2, 3, 'a', 'b', 'c']
これができると便利です。 を使ってジェネレータをリストに追加し+=
、ジェネレータの内容を取得できるからです。 との互換性がなくなるのは残念です+
が、仕方ありません。