【Python入門・応用】10.演算子 | プログラミングになくてはならない算術演算子や関係演算子、論理演算子などをしっかりとマスター

Python入門・応用10演算子サムネイル
Python入門・応用講座

こんにちは。キノコードです。
この動画では、Pythonの演算子について説明します。
説明する内容はこちらです。
足し算や掛け算などの算術演算子から学習し、関係演算子、論理演算子について学習しましょう。
また、代入演算子やis演算子、in演算子、ビット演算子、シフト演算子、と順に説明します。

さて、Python学習サービスの「キノクエスト」では、この動画で説明した内容の問題を解くことができます。
プログラミング学習者が集まるコミュニティがセットで1,990円です。
学習していてわからないことを、コミュニティで質問することができます。
コミュニティでは有志の方の勉強会も実施しています。
同じ目標を持つ方といっしょに学習を進めてみるのはいかがでしょうか?
詳しくは概要欄のURLをクリックしてご確認ください。

それではレッスンスタートです。

この記事の執筆・監修

キノコード
キノコード

テクノロジーアンドデザインカンパニー株式会社のCEO。
日本最大級のプログラミング教育のYouTubeチャンネル「キノコード」や、プログラミング学習サービス「キノクエスト」を運営。
書籍「あなたの仕事が一瞬で片付くPythonによる自動化仕事術」の著者や、雑誌「日経ソフトウエア」の寄稿などの実績も多数。

演算子とは

演算子とは、足し算、引き算などの四則演算や2つの値の大小を比較するときに使う記号のことです。

算術演算子

算術演算子から見てみましょう。
算術演算子とは、足し算、引き算、掛け算、割り算などをするための演算子です。
ここからは実際にコードを書きながら解説を進めます。

x = 10
y = 2

print(x + y)
print(x - y)
print(x * y)
print(x / y)
print(x % y)
print(x // y)

xという変数に10、yという変数に2を代入します。
この変数xと変数yを使って、演算子の役割を見てみましょう。
足し算と引き算はそのままプラスとマイナスです。
掛け算はアスタリスク( * )です。
割り算は、スラッシュ( / )です。
剰余は、パーセント( % )です。剰余とは、割り算の余りのことです。
そして、スラッシュが2つで割り算の商です。
実行して、表示させてみましょう。
足し算の結果は「12」、引き算は「8」、掛け算は「20」、割り算は「5」、剰余は「0」、商は「5」と表示されました。

関係演算子

次は関係演算子です。
関係演算子とは、2つの値の関係が真か偽か判断させる演算子のことです。
例えば、「20」は、「10」より大きいです。これを比較することができます。
関係演算子は、次のレッスンで学ぶ条件分岐のif文などでよく使います。
「10 > 2」は、「10」は「2」より大きいという意味です。
これは真です。
2つの関係が真のときには「True」が返ってきます。
コードを書いて見てみましょう。

x = 10
y = 2

print(x > y)

xという変数に10、yという変数に2を代入します。
x > yが真かどうかを調べてみましょう。
実行してみます。
Trueが表示されました。

x = 10
y = 2

print(x < y)

「x < y」としたらどうなるでしょうか?
つまり、「10」は「2」よりも小さいという意味です。
これは偽です。
偽のときの「False」が返ってきます。
実行してみます。
Falseが表示されました。

x = 10
y = 2
z = 10

print(x <= y)
print(x >= z)

「以上」場合は「>=」、、「以下」の場合は「<=」を使います。
zという変数に10を代入して、試してみましょう。
xはyより大きいのでFalse。
xはzと等しいのでTrueが返ってくるはずです。
実行してみましょう。
予想通りの結果となりました。

次に、等価について見ていきましょう。
等価とは、2つの値が等しいということです。
等価はイコールが2つです。
等価ではない場合は、エクスクラメーションマークにイコールです。
コードを書いて見てみましょう。

x = 10
y = 2

print(x == y)
print(x != y)

「10」と「2」は等価ではないので、偽の「Flase」が返ってくるはずです。
「10」と「2」は等価ではないので、こちらは真の「True」が返ってくるはずです。
実行してみましょう。
それぞれ「False」と「True」が返ってきました。

論理演算子

次は論理演算子について見てみましょう。
論理演算子とは、複数の条件を判断させる演算子のことです。
日本語で言うと「かつ」と「または」ということです。英語で言うとandとorです。
and条件はandを記述し、or条件はorを記述します。
「3」と「8」いう数字で見ていきましょう。
例えば、「8」という数字は、「5以上かつ10以下」です。
「3」という数字は、「5以上かつ10以下」ではないです。
コードを書いて見てみましょう。

x = 8
y = 3

print(x >= 5 and x <= 10)
print(y >= 5 and y <= 10)

まずはand条件についてです。
「8」は5以上かつ10以下なので「True」、「3」は5以上かつ10以下ではないので「False」が返ってくるはずです。
実行してみましょう。
それぞれ「True」と「False」が返ってきました。

x = 8
y = 3

print(x == 3 or y == 3)
print(x == 1 or y == 1)

次にor条件についてです。
「xは3と等しい」かまたは、「yは3と等しい」という条件です。
これはyが3なので、後ろの条件が一致します。
Trueが返ってくるはずです。
「xは1と等しい」かまたは、「yは1と等しい」という条件も書いてみましょう。
こちらはどちらの条件にも一致しないので、Falseが返ってくるはずです。
実行してみましょう。
予想通りの結果が返ってきました。

代入演算子、複合代入演算子

次に、代入演算子です。
今まで変数に代入するときに使っていた「=」は代入演算子と言います。
また、代入するときに、足し算や引き算を同時にすることができます。
足し算、引き算などと組み合わせて代入する演算子のことを複合代入演算子と言います。
例えば、xという変数に「10」を足してから、xに代入する方法は、「x += 10」です。
これはx = x + 10と同じ意味です。
例えば、zにyを足してからzに代入する方法は、「z += y」です。
コードを書いて見てみましょう。

x = 8
y = 12
z = 20

x += 10
z += y

print(x)
print(z)

xという変数に8、yという変数に12、zという変数に20を代入して、複合代入演算子を試してみましょう。
xは18となり、zは32となるはずです。
実行してみましょう。
予想通りの結果が返ってきました。

is演算子

次に、is演算子について説明します。
is演算子は関係演算子の一種で、2つのオブジェクトが同じかどうかを判定する演算子です。
Pythonのオブジェクトとは、文字列、リスト、辞書、タプル、整数等の型を持つデータのことです。
また、関係演算子のところで説明した、==(イコール2つ)の演算子はオブジェクトの値が同じがどうかを判定する演算子です。
関係演算子の==(イコール2つ)を使用すると、違うオブジェクトでも値が同じであれば、Trueが表示されます。
この2つの演算子の違いを、具体例で確認してみましょう。

l1 = [1, 2, 3, 4, 5]
l2 = [1, 2, 3, 4, 5]
l3 = l1

print("l1 == l2 :", l1 == l2)
print("l1 is l2 :", l1 is l2)
print("l1 == l3 :", l1 == l3)
print("l1 is l3 :", l1 is l3)

変数l1と変数l2に同じ値が入ったリストを代入します。
また、変数l3にl1を代入します。
このl1、l2の関係と、l1、l3の関係を==(イコール2つ)の演算子で判定した場合の結果と、is演算子で判定した場合の結果を表示してみましょう。
結果がわかりやすいように、print関数の引数に判定式の文字列を指定してから、判定式を指定します。
実行します。
l1とl2は同じ値を持っているので、==(イコール2つ)の演算子で判定した場合はTrueとなります。
しかし、l1とl2は、値は同じでも違うオブジェクトなので、is演算子で判定した場合はFalseとなります。
一方、l3はl1を代入しているので、l1とl3は同じ値を持ち、かつ同じオブジェクトです。
よって、==(イコール2つ)の演算子で判定した場合も、is演算子で判定した場合もTrueとなります。

オブジェクトが同じかどうかは、オブジェクトのidを確認するとわかります。
すべてのオブジェクトには、固有の番号、つまりIDが振り分けられています。
idは、id関数で確認することができます。

l1 = [1, 2, 3, 4, 5]
l2 = [1, 2, 3, 4, 5]
l3 = l1

print("l1_id :", id(l1))
print("l2_id :", id(l2))
print("l3_id :", id(l3))

id関数を使用して、l1、l2、l3のidがどのようになっているか確認してみましょう。
引数にidを確認したいオブジェクトを指定します。
実行します。
l1とl2は違うidを持っており、l1とl3は同じidを持っていることがわかります。
そのため、l1とl2は違うオブジェクト、l1とl3は同じオブジェクトということが確認できました。

リストは要素の値に関わらず、作成された時点でリストオブジェクトとして固有のidを持ちます。
また、リストは変更可能なオブジェクトなため、リストの要素を変更してもidは変わりません。
確認してみましょう。

l1 = [1, 2, 3, 4, 5]
l2 = [1, 2, 3, 4, 5]
l3 = l1

print(l1, id(l1))
l1.append(6)
print(l1, id(l1))

print(l2)
print(l3)

先ほどのl1の末尾に6を追加し、変更前のリストとid、変更後のリストとidを表示します。
また、l1の要素を変更したときに、l2とl3がどのようになるか確認してみましょう。
実行します。
リストの場合、要素が変わってもidは変わらないことがわかります。
また、l1とl2は違うオブジェクトなため、l1の要素を変更してもl2の要素は変わりません。
一方、l1とl3は同じオブジェクトなため、l1の要素を変更するとl3の要素も変わります。
オブジェクトのidという考え方は少しややこしいですが、覚えておきましょう。

is not演算子

続いて、is not演算子について説明します。
is not演算子はis演算子の逆で、2つのオブジェクトが違う場合にTrueを返し、同じ場合にFalseを返します。
確認してみましょう。

l1 = [1, 2, 3, 4, 5]
l2 = [1, 2, 3, 4, 5]
l3 = l1

print("    l1 is l2 :", l1 is l2)
print("l1 is not l2 :", l1 is not l2)
print("    l1 is l3 :", l1 is l3)
print("l1 is not l3 :", l1 is not l3)

is演算子の説明で用いたリストと変数を使用します。
それぞれis演算子を使用した場合と比較して、結果を表示してみましょう。
実行します。
l1とl2は違うオブジェクトなので、is演算子を用いた場合はFalse、is not演算子を用いた場合はTrueが表示されます。
一方、l1とl3は同じオブジェクトなので、is演算子を用いた場合はTrue、is not演算子を用いた場合はFalseが表示されます。

in演算子

次に、in演算子について説明します。
in演算子も関係演算子の一種で、リストやタプルなど、複数の要素を持つオブジェクトの中に、特定の要素が含まれているかどうかを判定する演算子です。
具体例で確認してみましょう。

l = ['Yamada', 'Sato', 'Takahashi']

print("Yamada in l :", "Yamada" in l)
print("Suzuki in l :", "Suzuki" in l)

Yamadaさん、Satoさん、Takahashiさんを要素に持つリストを作成し、変数lに代入します。
Yamadaさんがlに含まれているか、Suzukiさんがlに含まれているかをin演算子で判定し、結果を表示してみましょう。
実行します。
Yamadaさんはlに含まれているのでTrue、Suzukiさんはlに含まれていないのでFalseが表示されます。

not in演算子

続いて、not in演算子について説明します。
not in演算子はin演算子の逆で、複数の要素を持つオブジェクトの中に、特定の要素が含まれていない場合にTrueを返し、含まれている場合にFalseを返します。
確認してみましょう。

l = ['Yamada', 'Sato', 'Takahashi']

print("    Yamada in l :", "Yamada" in l)
print("Yamada not in l :", "Yamada" not in l)
print("    Suzuki in l :", "Suzuki" in l)
print("Suzuki not in l :", "Suzuki" not in l)

in演算子の説明で用いたリストを使用します。
それぞれin演算子を使用した場合と比較して、結果を表示してみましょう。
実行します。
Yamadaさんはlに含まれているので、in演算子を用いた場合はTrue、not in演算子を用いた場合はFalseが表示されます。
一方、Suzukiさんはlに含まれていないので、in演算子を用いた場合はFalse、not in演算子を用いた場合はTrueが表示されます。

ビット演算子

次に、ビット演算子について説明します。
ビット演算子は、2進数で表した数値を0と1の羅列とみなして論理演算をする演算子です。
まず、2進数の表記について説明します。

print(0b10)
print(0b1000)
print(0b1010)

2進数を表記する場合は、先頭に0、そして小文字のbを記述します。
これが2進数表記の目印になります。
その後ろに0か1の数値を続けて記述します。
実際に2進数で数値を記述し、表示してみましょう。
結果は自動的に10進数に変換して表示されます。
実行します。
「1,0」は10進数の2、「1,0,0,0」は10進数の8、「1,0,1,0」は10進数の10を表していることがわかります。

また、bin関数を使用すると、10進数を2進数に変換することができます。

print(bin(2))
print(bin(8))
print(bin(10))

引数に変換したい数値を指定します。
10進数の2,8,10を2進数に変換して表示してみましょう。
実行します。
2進数に変換できました。

この0と1の羅列のことをビット列と呼びます。
ビット演算ではそれぞれの位の値が0か1かという情報だけ使用します。
また、ビット演算には、論理和、論理積、排他的論理和、論理否定、シフト演算の5つがあります。
一つずつ確認していきましょう。

論理和

まず、論理和について説明します。
論理和は|(バーティカルライン)で表し、同じ位にあるビットのうち、少なくともどちらか一方が1なら1を返し、それ以外は0を返します。
集合における和集合と同じです。

x = 0b1010
y = 0b0110

a = x | y

print(bin(a))

このような2進数で表されたxとyで考えてみましょう。
変数xに2進数の1,0,1,0を代入し、変数yに2進数の0,1,1,0を代入します。
そして、xとyの論理和を変数aに代入します。
print関数でそのまま表示すると、自動的に10進数に変換して表示されるので、bin関数を使用して2進数に変換して表示しましょう。
実行します。
2進数で、1,1,1,0と表示されました。
4桁目はxが1なので1、3桁目はyが1なので1、2桁目はxとyが両方1なので1、1桁目はxとyが両方0なので0になります。

論理積

続いて、論理積について説明します。
論理積は&(アンパサンド)で表し、同じ位にあるビットのうち、両方とも1なら1を返し、それ以外は0を返します。
集合における積集合と同じです。
確認してみましょう。

x = 0b1010
y = 0b0110

a = x & y

print(bin(a))

先ほどと同じ2進数の値を変数xと変数yに代入し、xとyの論理積を変数aに代入します。
そして、bin関数を使用して、aを2進数に変換して表示してみましょう。
実行します。
2進数で、1,0と表示されました。
4桁目はyが0なので0、3桁目はxが0なので0、2桁目はxとyが両方1なので1、1桁目はxとyが両方0なので0になります。
なお、10進数と同様に、デフォルトでは高い位にある0は省略されます。

排他的論理和

続いて、排他的論理和について説明します。
排他的論理和は^(ハット)で表し、同じ位にあるビットのうち、どちらか1つだけ1の場合に1を、それ以外は0を返します。
集合における対称差集合と同じです。
確認してみましょう。

x = 0b1010
y = 0b0110

a = x ^ y

print(bin(a))

先ほどと同じ2進数の値を変数xと変数yに代入し、xとyの排他的論理和を変数aに代入します。
そして、bin関数を使用して、aを2進数に変換して表示してみましょう。
実行します。
2進数で、1,1,0,0と表示されました。
4桁目はxが1、yが0なので1、3桁目はxが0、yが1なので1、2桁目はxとyが両方1なので0、1桁目はxとyが両方0なので0になります。

論理否定

続いて、論理否定について説明します。
論理否定は~(チルダ)で表し、0と1を反転させた値を返します。
ただし、Pythonにおける論理否定は少し複雑です。
一つずつ説明していきます。

補数

まず、補数の説明をします。
補数とは、元の数に足すと桁が1つ上がる数のうち、最小の数値のことです。
10進数の場合で考えてみましょう。
例えば、元の数が6の場合、4を足すと桁が1つ上がって10になります。
よって、6の補数は4になります。
また、元の数が80の場合、20を足すと桁が1つ上がって100になります。
よって、80の補数は20になります。
今度は2進数の場合で考えてみましょう。
元の数が1,1,1の場合、0,0,1を足すと桁が1つ上がって1,0,0,0になります。
よって、1,1,1の補数は、0,0,1になります。
また、元の数が1,1,0の場合、0,1,0を足すと桁が1つ上がって1,0,0,0になります。
よって、1,1,0の補数は、0,1,0になります。
2進数の場合は慣れていないと計算しづらいかもしれませんが、元の数の0と1を反転させて、さらに1を足した値が補数になります。

また、元の数と補数を足すと、最上位の桁だけ1になり、残りは0になります。
すなわち、最上位の桁を無視すると0になります。
元の数と補数を足して0になるということは、補数は元の数のマイナスの値を表せそうです。
この性質を利用すると、マイナス記号を使わずに、補数を使って元の数のマイナスの値を表現できます。
具体例を出します。
まず、10進数で考えてみましょう。
前提として、数値は1桁しか考えないとします。
例えば、7マイナス6は1になります。
これはマイナス記号を使った考え方です。
ここで6の補数を考えましょう。
6の補数は4になります。
マイナス6の代わりにプラス4をします。
7プラス4は11になります。
数値は1桁しか考えないという前提があるので、2桁目は桁溢れとして無視すると、1になります。
これは7マイナス6の結果と同じです。
つまり、6をマイナスする代わりに、6の補数である4をプラスして最上位の桁を無視すると、6をマイナスした結果と同じ計算結果になります。
すなわち、補数は元の数のマイナスの値を表している、と考えることができます。
この性質は2進数でも同じです。
前提として、3桁しか考えないとします。
例えば、1,1,1から0,1,1をマイナスすると1,0,0になります。
ここで0,1,1の補数を考えましょう。
0,1,1の補数は1,0,1になります。
0,1,1をマイナスする代わりに1,0,1をプラスします。
1,1,1プラス1,0,1は1,1,0,0になります。
数値は3桁しか考えないという前提があるので、4桁目は桁溢れとして無視すると、1,0,0になります。
これは1,1,1から0,1,1をマイナスした結果と同じです。
このように、最上位の数は桁溢れとして無視すると、元の数をマイナスした計算結果と、元の数の補数をプラスした計算結果は同じになります。
このことから、多くのコンピューターでは、補数は元の数のマイナスの値として認識されます。

Pythonでの表示方法

補数がわかったところで、Pythonでの表示方法を確認してみましょう。
Pythonで2進数を記述すると、上の桁が無限に0で埋められており、その0が省略されているとみなされます。
従って、論理否定で0と1を反転させると、上の桁が無限に1で埋められます。
しかし、1は省略できず、さらに無限個の1は表示できません。
そこで補数を使用します。
補数は元の数のマイナスの値として認識されます。
符号を元に戻すために、さらにマイナスを付けます。
この結果がPythonの論理否定で表示される値になります。

それでは、Pythonで論理否定の演算子を使用してみましょう。

x = 0b1010
y = 0b0110

a = ~x
b = ~y

print(bin(a))
print(bin(b))

今までと同様に2進数で表された数値を変数xと変数yに代入します。
次に、xとyの論理否定を変数aと変数bに代入します。
aとbを2進数で表示してみましょう。
実行します。
2進数で、aはマイナス1,0,1,1と表示され、bはマイナス1,1,1と表示されました。
最初に、aの結果から説明します。
まず、xは上の桁が無限に0で埋められていると考えるので、0,0,0...1,0,1,0となります。
論理否定では、この0と1を反転させます。
すると、上の桁が無限に1で埋められた、1,1,1...0,1,0,1となります。
この数値は表示できないので、この数値の補数を考えます。
補数は1,0,1,1になります。
補数は元の数と符号が逆になるので、元に戻すためにマイナスを付けます。
この結果がaに表示されます。

bの結果も同様です。
まず、yの上の桁が無限に0で埋められていると考えるので、0,0,0...0,1,1,0となります。
そして、この0と1を反転させます。
すると、1,1,1...1,0,0,1となります。
この数値の補数を考えます。
補数は1,1,1となります。
この補数にマイナスを付けます。
この結果がbに表示されます。

Pythonではこのように表示されますが、求めたいのは元の数の0と1を反転させた値です。
結果が求めたい値にならない原因は、無限に並んだ必要のない上の桁も表示しようとしていることにあります。
そこで、ビットマスクと呼ばれる、必要な部分だけを取り出す方法を使うとこの問題を解決できます。
ビットマスクは、必要な部分に1、必要のない部分に0を並べた値との論理積を取ります。
すると、必要のない部分は0になり、必要な部分だけそのまま表示されます。
先ほどのxとyで考えると、必要な部分は末尾の4桁だけで、無限に続くそれより上の桁は必要ありません。
よって、論理否定で0と1を反転させた値と2進数の1,1,1,1の論理積を取れば、求めたい値になります。
なお、2進数の1,1,1,1は上の桁に無限に続く0が省略されていると考えます。
確認してみましょう。

x = 0b1010
y = 0b0110

a = ~x
b = ~y

print(bin(a & 0b1111))
print(bin(b & 0b1111))

先ほどと同じ2進数で表された数値を変数xと変数yに代入します。
次に、xとyの論理否定を変数aと変数bに代入します。
そして必要な部分は末尾の4桁だけなので、2進数の1,1,1,1との論理積を取り、2進数で表示してみましょう。
実行します。
xの0と1を反転させた1,0,1と、yの0と1を反転させた1,0,0,1が表示されました。
このように、ビットマスクを使用すると、元の数を反転させた値を表示できます。

シフト演算子

最後に、シフト演算子について説明します。
シフト演算子は>>(大なり2つ)または<<(小なり2つ)で表し、それぞれの位のビットを右や左にシフトさせます。
確認してみましょう。

x = 0b1010
y = 0b0110

a = x >> 2
b = y << 3

print(bin(a))
print(bin(b))

先ほどと同じ2進数で表された数値を変数xと変数yに代入します。
x、>>(大なり2つ)、シフトさせる数、でxの各位のビットを右方向にシフトできます。
xを右方向に2つシフトさせた値を、変数aに代入します。
また、yを左方向に3つシフトさせた値を、変数bに代入します。
aとbを2進数に変換して表示してみましょう。
実行します。
xを右方向に2つシフトさせると、1,0となり、位がなくなった右端は切り捨てられます。
yを左方向に3つシフトさせると、1,1,0,0,0,0となり、右端は0で補完されます。

おわりに

レッスンは以上です。
わかりにくいところはありませんでしたでしょうか?
復習や基礎固めには「キノクエスト」をご活用ください。
何度も問題を解くことで、知識の定着ができます。
問題数は約700問、腕試しをしてみてはいかがでしょうか?
詳しくは概要欄のURLをクリックしてご確認ください。
キノクエストのご登録をお待ちしております。

次のレッスンでは、こちらについて説明します。
それでは、次の動画でお会いしましょう。