拡張代入とは、他の言語にもみられるように、数値を足す時に += や -=を用いて変数に値を代入することである。
例えば、数値型の変数に、ある値を代入しようとしたときに
num = 10
num = num + 5
num = num + 5
という、基本的な演算は
num = 10
num += 5
と拡張代入を用いて表せる。 num += 5
このとき、拡張代入を行うときの利点といえば、+=で演算するときに、演算子の左側の変数numを二度評価しないですむため、その分処理が早くなることである。
数値においては直感的に理解できると思う。
そして、拡張代入で面白いことといえば、演算対象のオブジェクトが可変性か不変性かによって、裏側で速度的に有利な処理方法が自動的に選択されることである。
ある可変性のオブジェクトであるリストに要素を追加する時は普通の+演算子を使う場合や、リストオブジェクトのappendメソッドを使うなど、複数の方法があります。
そこで、以下のリストに要素を追加する際に、どの方法で処理を行うかによって細かい点で違いが生じます。
L = [1,2]
L = L + [3]
この場合、Lを出力すると [1,2,3]になります。 =を使って要素を追加することを連結といい、代入前のLのオブジェクトとはまた異なるオブジェクトが生成され、あらたなL[1,2,3]が生成されます。L = L + [3]
この場合、いちいち新たにオブジェクトを生成していて、オブジェクトの生成には処理的に負荷があるので、低速です。
ところが、appendメソッドを使って要素を追加した場合、新たにオブジェクトが生成されるのでなく、元のリストの参照を上書きして、対象のオブジェクトのメモリブロックの最後に要素が追加されるだけなので、=を使う時より高速に処理できます。
そしてさらにもう一つの方法として、先ほどの拡張代入を用いると、プログラマがわざわざ連結を使うかメソッドを使うか判断する必要がなく、裏側で速度的に有利な方法で処理してくれます。
拡張代入での演算の注意点
ひとつ気をつけなければいけないのが、先ほど説明したように、通常の+を使った連結とは異なるということです。
拡張代入を使った演算は、演算対象の変数を上書きしてしまうので、もし複数の変数で同じリストを参照したときに、その参照している変数全体に影響が及びます
L = [1,2,3] M = L #MはLと同じリストを参照している
L = L + [10] #Lを連結する
L, M # Lは[1,2,3,10] Mは[1,2]
L = L + [10] #Lを連結する
L, M # Lは[1,2,3,10] Mは[1,2]
L = [1, 2]
M = L L += [3,4]
L,M #Mも同時に変更され、どちらも [1,2,3,4]
リストやディクショナリを扱った演算を行う場合は、意図しない処理になってしまうので、このときの対策法として、 可変性のオブジェクトの場合は、別の変数をつくって別々に演算することです。
M = L L += [3,4]
L,M #Mも同時に変更され、どちらも [1,2,3,4]