3.5 代入と代入演算子

(1/1)
そろばんを使う女の子のイラスト
Pythonの代入 = について詳しく解説し、算術演算子と代入を組み合わせた代入演算子について学ぶ。また、他のプログラミング言語のようなインクリメント演算子、デクリメント演算子がないことについて触れる。

目次

サンプル・プログラム

代入

これまで見てきたように、Python におけるイコール = は等号を表すのではなく、代入という作用を持つ。 \( op1 = op2 \) のようにして用い、オペランド \( op2 \) を \( op1 \) に代入する。たとえば \( x=1+2 \)なら、\( 1+2 \) の計算結果を変数 \( x \) に代入する。

少しハードウェア寄りの話をすると、代入 = は、右のオペランドが格納されているアドレス(メモリ上の場所を示す住所のような値)を変数に代入する。
# リスト
a = [1, 2, 3]	# リストa
b = a			# リストb
print("a = " + str(a))
print("b = " + str(b))

# 比較演算
print("a is b : " + str(a is b))

# ミュータブル
a[0] = 99
print("a = " + str(a))
print("b = " + str(b))

# 比較演算
print("a is b : " + str(a is b))
ミュータブル
ミュータブル
たとえばプログラム "mutable.py" では、リスト [1, 2, 3] が格納されているアドレスをかりに300番地とすると、変数 a には300番地を代入する。次に、変数 a の値を変数 b に代入するので、変数 b にも300番地が入る。つまり、変数 ab は同じリストの実体 [1, 2, 3] を指していることになる。

このため、変数 a の冒頭の値に99を再代入してやると、それに連動して変数 b の冒頭の値も変わる。これをミュータブル(変更可能)という。

「3.3 比較演算子とブール演算子」で紹介したように、is演算子を使ってみると、再代入の前後で同一性が失われていないことが分かる。
# int
a = 1		# int a
b = a		# int b
print("a = " + str(a))
print("b = " + str(b))

# 比較演算
print("a is b : " + str(a is b))

# イミュータブル
a = 99
print("a = " + str(a))
print("b = " + str(b))

# 比較演算
print("a is b : " + str(a is b))
イミュータブル
イミュータブル
プログラム "immutable.py" も同様に、整数 1 が格納されているアドレスをかりに400番地とすると、変数 a には400番地を代入する。次に、変数 a の値を変数 b に代入するので、変数 b にも400番地が入る。この時点では、変数 ab は同じ整数の実体 1 を指していることになる。

ところが、変数 a に99に再代入しても、変数 b は変化しない。これをイミュータブル(変更不可)という。

is演算子を使ってみると、最初は同一性であったが、再代入後に同一性が失われていることが分かる。

複合代入演算子

Python は、他のプログラミング言語と同様、代入と算術演算子と組み合わせた複合代入演算子(単に代入演算子と呼ぶことも)が用意されている。たとえば a += 2 は、変数 \( a \) に2を加算し、変数 \( a \) に代入するという働きをする。
代入演算子
演算子意味
+=左辺と右辺を加算した結果を左辺に代入する
-=左辺から右辺を減算した結果を左辺に代入する
*=左辺と右辺を乗算した結果を左辺に代入する
/=左辺を右辺で除算した結果を左辺に代入する
//=左辺を右辺で除算した結果(整数部)を左辺に代入する
%=左辺を右辺で除算した剰余をを左辺に代入する
**=左辺を右辺をべき乗した結果を左辺に代入する
&=左辺と右辺をビット論理積した結果を左辺に代入する
|=左辺と右辺をビット論理和した結果を左辺に代入する
^=左辺と右辺をビット排他的論理和した結果を左辺に代入する
>>=左辺を右辺の値だけ右シフトした結果を左辺に代入する
<<=左辺を右辺の値だけ左シフトした結果を左辺に代入する

新しい代入演算子

これまで、算術演算子、ビット演算子、シフト演算子、代入演算子を学んできたが、これらは、かならず「計算結果」を返す。
一方、Python の代入 = を、わざと代入演算子と呼ばなかったのは、他のプログラミング言語と違って計算結果を返さないからだ。代入 = は演算子ではなく、文法構造の1つなのである。
たとえば JavaScript、PHP、C++などで
b = (a = 2) * 3
を計算すると、\( a = 2, b = 6 \) となる。ところが、Python ではエラーになってしまう。
Python では = が値を返さないため、式のエラーになっているのだ。

そこで、Python 3.8では、他言語の代入演算子と同じ働きをする 代入式 := が導入された。
# 代入式
b = (a := 2) * 3
print("a = " + str(a))
print("b = " + str(b))

インクリメントとデクリメントはない

他のプログラミング言語にある i++ のようなインクリメント演算子や、i-- のようなデクリメント演算子が、Python には用意されていない。だが、これまで学んできた加算代入演算子 i += 1 や減算代入演算子 i -= 1 を使って代用できることが分かるだろう。

インクリメント/デクリメント演算子は、前置式 ++i にするか、後置式 i++ にするかで式の評価の順序が変わるため、シンプルなプログラミングを目指す Pythonでは実装されなかった。

アンパック代入

代入 = が文法構造であることを端的に示すのがアンパック代入だ。
Python では、たとえばリストの要素を代入 = を使って分解して、別々の変数に代入することができる。これをアンパック代入と呼ぶ。
# アンパック代入(1)
a = [1, 2, 3]
x, y, z = a
print("x = " + str(x))
print("y = " + str(y))
print("z = " + str(z))
アンパック代入で、変数名にアスタリスク * を付けると、その他のデータを格納するという意味になる。
# アンパック代入(2)
a = [1, 2, 3, 4, 5]
x, *y, z = a
print("x = " + str(x))
print("y = " + str(y))
print("z = " + str(z))
アンパック代入を応用すると、簡単に変数の内容を入れ替える(スワップする)ことができる。
a = [1, 2, 3]
b = [7, 8, 9]
print("a = " + str(a))
print("b = " + str(b))

# スワップ
a, b = b, a
print("a = " + str(a))
print("b = " + str(b))

練習問題

次回予告

次回は、Pythonの演算子の優先順位を学ぶ。算術演算子、論理演算子、ビット演算子、シフト演算子、代入演算子について学んできたが、算数で乗除算が加減算に優先するように、これらの演算子が1つの式の中に含まれるときには演算の優先順位がある。演算子が多いので覚えきれないかもしれないが、大丈夫――算数と同じで括弧 (...) を付けることで優先順位をコントロールできるので。

コラム:COBOLとBASICのイコール

Python を含む多くのプログラミング言語では、比較演算子で等しいかどうかを計算するのに == を使うが、COBOL言語ではイコール = である。その代わり、代入は MOVE を使う。

厄介なのは BASIC言語で、比較演算子 = と代入 = が同じである。つまり
If a = b Then
これは変数aとbの値を比較する比較演算であるが、
a = b
は変数bをaに代入する代入式となる。
では、
Console.WriteLine(a = b)
は、どちらの作用かというと‥‥比較演算である。

COBOL言語BASIC言語を習ったことがある方は、比較演算をイコール = にしないよう気をつけよう。
(この項おわり)
header