【自前python講座】条件分岐/繰り返し処理/例外処理

python-flow-control プログラミング
python-flow-control



自前の python 講座用資料です.
今回は,条件分岐(if-elif-else)/繰り返し処理(for-in-else, while-else)/例外処理(try-except-else-finally)についてコードを交えて説明していきます.
これまで勉強した条件式やメソッドや組み込み関数が活躍し,プログラミングらしくなってきます.
実務への運用も見えてきて楽しくなってくるので,最後に掲載する演習問題もプログラミングしていただいて,コードを書いて実行して手を動かしながら身に着けていきましょう.

今回のコードは,こちらの github にも載せています.

https://github.com/KazutoMakino/PythonCourse/blob/main/003_control_flow/003_control_flow.ipynb

条件分岐: if-elif-else

if 条件式:
    処理
elif 条件式:
    処理
elif 条件式:
    処理
else:
    処理

構文は上記の通りで,if の条件式を満たす場合はコロン : の次のインデントブロック(字下げされた行)が処理されます.
ここで,python におけるインデントはスペース 4 文字ということが,python のコーディング規約の PEP8 によって定められています.
elif は if の条件式が False だった場合に上から順番に if のように条件分岐でき,0 を含む任意個数が使用可能です.
C 言語で言う switch 文も elif を用いて作成します.
else は 0 もしくは 1 回利用可能で,どの if-else の条件式にも適合しなかった場合に処理されます.
例えば以下のように使います.

a = 3

if a == 1:
    print("a == 1")
elif a == 2:
    print("a == 2")
elif a < 3:
    print("a < 3")
else:
    print("else")

# else
a = ["a", "bb", "ccc", "dddd"]

if "a" not in a:
    print(1)
elif "bb" not in a:
    print(2)
elif "ccc" not in a:
    print(3)
elif "dddd" in a:
    print(4)
else:
    print("else")

# 4

処理において,特に何もしない pass というオブジェクトも使うことができます.
例えば,出力はしないけど,明示的に個々の処理は OK ということをコードで残したい時に使われると思います.

a = True

if a is True:
    pass
else:
    print("else")

繰り返し処理: for-in-else

for value in iterable:
    処理
else:
    処理

構文は上記の通りで,リストや range() などの iterable オブジェクトが一度だけ回され,value に代入されます.
: の次のインデントブロックは,value の個数回処理されます.
else は 0 もしくは 1 回利用可能で,for の処理が終わった際に処理されます.
繰り返しを break で抜けた場合,else のインデントブロックは処理されずスキップされます.
例えば以下のように使います.

for i in [0, 1, 2]:
    print(i)

# 0
# 1
# 2
fruits = ["banana", "apple", "orange"]
for fruit in fruits:
    print(fruit)

# banana
# apple
# orange
for i in range(5):
    print(i)
else:
    print("end")

# 0
# 1
# 2
# 3
# 4
# end
for i, fruit in enumerate(fruits):
    if fruit.endswith("e"):
        print(i, fruit)

# 1 apple
# 2 orange
metal = {"gold": "Au", "silver": "Ag", "bronze": "Cu and Sn"}

for metal_name, symbol in metal.items():
    print(metal_name, symbol)
else:
    print("銅メダルは青銅なので bronze")

# gold Au
# silver Ag
# bronze Cu and Sn
# 銅メダルは青銅なので bronze
for i,j in zip(range(10), range(10,20,1)):
    print(i,j)

# 0 10
# 1 11
# 2 12
# 3 13
# 4 14
# 5 15
# 6 16
# 7 17
# 8 18
# 9 19
for i,j in zip(range(10), range(10,20,1)):
    if (i==0) or (i % 2 == 1):
        continue
    if j>=17:
        break
    print(i,j)

# 2 12
# 4 14
# 6 16
arr = []

for i in range(5):
    arr.append([])
    for j in range(5):
        arr[i].append(i*j)
arr

# [[0, 0, 0, 0, 0],
#  [0, 1, 2, 3, 4],
#  [0, 2, 4, 6, 8],
#  [0, 3, 6, 9, 12],
#  [0, 4, 8, 12, 16]]
for i in range(5):
    print(i)
    if i > 3:
        break
else:
    print("not print")

# 0
# 1
# 2
# 3
# 4

繰り返し処理: while-else

while 条件式:
    処理
else:
    処理

構文は上記の通りで,条件式が True を返す間中ずっと直後のインデントブロックの処理を繰り返し続け,条件式が False になると else のインデントブロックを処理します.
条件式が True しかない場合は無限ループになります.
無限ループが好ましくない場合は,while の条件式に対してインデントブロックの計算結果が関与して,while を抜けられるような条件式となること,あるいは,while のインデントブロック内で break を用いることで,無限ループを回避できます.
繰り返しを break で抜けた場合,else のインデントブロックは処理されずスキップされます.

注意として,待ち時間なしの画面出力を含む無限ループを走らせた場合,ひっきりなしに画面出力が際限なく行われます.
これを停止したい場合は,jupyter であれば上部タスクバーの四角ボタン (Interrupt the kernel) をクリックもしくはブラウザのタブを閉じます.
コマンドライン上では,ctrl キーと C キーを同時に押下するか,違うターミナルを起動し,コマンドラインでプロセスを強制終了させたり,もしくは,タスクマネージャーでプロセス終了を行っても良いでしょう.

使用例は以下です.

i = 0
while i < 3:
    print(i)
    i += 1
else:
    print("aaa")

# 0
# 1
# 2
# aaa
i = 0
while True:
    print(i)
    if i > 3:
        print("break")
        break
    i += 1
else:
    print("not print")

# 0
# 1
# 2
# 3
# 4
# break
arr = []
i = 0
while i < 5:
    arr.append([i, i**i])
    print(arr)
    i += 1

# [[0, 1]]
# [[0, 1], [1, 1]]
# [[0, 1], [1, 1], [2, 4]]
# [[0, 1], [1, 1], [2, 4], [3, 27]]
# [[0, 1], [1, 1], [2, 4], [3, 27], [4, 256]]

例外処理: try-except-else-finally

その名の通り例外処理は,処理にエラー(例外)が生じたときに異なる処理をさせる制御機能です.
例外処理の前に,まずは,エラーについて少し知っておく必要があります.
例えば,1 / 0 は無限大に発散しますが,数値計算上では無限大という属性,もしくは,エラーとなります.
python の標準ライブラリにおいては,「0 で割れません」ということで ZeroDivisionError が送出されます.
また,1 + "1" は整数と文字列の足し算を行っていますが,実際には処理できず,「演算対象の型に問題がある」ということで TypeError が送出されます.
コードで実行してみると以下の通りです.

a = 1 / 0

# ---------------------------------------------------------------------------
# ZeroDivisionError                         Traceback (most recent call last)
# ~\AppData\Local\Temp/ipykernel_632/3979469288.py in <module>
# ----> 1 a = 1 / 0
# 
# ZeroDivisionError: division by zero
a = 1 + "1"

# ---------------------------------------------------------------------------
# TypeError                                 Traceback (most recent call last)
# ~\AppData\Local\Temp/ipykernel_632/273750274.py in <module>
# ----> 1 a = 1 + "1"
# 
# TypeError: unsupported operand type(s) for +: 'int' and 'str'

上記を見越して例外処理させたい場合に,以下のような構文を用います.

try:
    処理
except エラー名称 as 左記のエラーを示す任意変数名:
    処理
else:
    処理
finally:
    処理

初めに try のインデントブロックが処理されます.
この処理でエラーが送出され,かつ,このエラーと except で指定したエラー名称が一致する場合,except のインデントブロックが処理されます.
except と エラー名称 は必須です.
try のインデントブロックの処理におけるエラーと except で指定したエラーと異なる場合は,そのままエラーが送出され,処理が終了します.
else のインデントブロックは,try でエラーが送出されないで正常終了する場合に,try の処理がすべて終了した後に処理されます.つまり,try でエラーが送出され except に処理がわたる場合は,else は無視されます.
finally のインデントブロックは,try / except / else の処理が全て終了した後に処理されます.
else と finally はオプションのため,必須ではありません.
例を以下に示します.

try:
    a = 1 / 1
    print(a)
except ZeroDivisionError:
    print("ZeroDivisionError")

# 1.0

この場合は,エラーが無いので a がそのまま出力されています.

try:
    a = 1 / 1
    print(a)
except ZeroDivisionError:
    print("ZeroDivisionError")
else:
    print("else")

# 1.0
# else

try のインデントブロックが正常終了しているので,else が処理されています.

try:
    a = 1 / 0
    print(a)
except ZeroDivisionError:
    print("ZeroDivisionError")
else:
    print("else")

# ZeroDivisionError

try のインデントブロックが ZeroDivisionError を送出し,かつ,except にて指定したエラーと同一なので,except が処理されています.
一方,正常終了していないので,else は処理されません.

try:
    a = 1 / 0
    print(a)
except ZeroDivisionError as err:
    print(err)

# division by zero

ZeroDivisionError を変数 err として定義し,表示しています.

try:
    a = 1 / 0
    print(a)
except TypeError as err:
    print(err)

# ---------------------------------------------------------------------------
# ZeroDivisionError                         Traceback (most recent call last)
# ~\AppData\Local\Temp/ipykernel_632/1181306212.py in <module>
#       1 try:
# ----> 2     a = 1 / 0
#       3     print(a)
#       4 except TypeError as err:
#       5     print(err)
# 
# ZeroDivisionError: division by zero

except にて TypeError を指定しましたが,実際は ZeroDivisionError だったので,except にて例外がキャッチされず,try のエラーがそのまま出力されています.

try:
    a = 1 / 1
    print(a)
except ZeroDivisionError as err:
    print(err)
else:
    print("else")
finally:
    print("finally")

# 1.0
# else
# finally

正常終了しているので else が処理され,かつ,finally が処理されています.

try:
    a = 1 / 0
    print(a)
except ZeroDivisionError as err:
    print(err)
else:
    print("else")
finally:
    print("finally")

# division by zero
# finally

異常終了しているので else が処理されず,finally は処理されています.

try:
    a = 1 / 0
    print(a)
except TypeError as err:
    print(err)
else:
    print("else")
finally:
    print("finally")

# finally
# ---------------------------------------------------------------------------
# ZeroDivisionError                         Traceback (most recent call last)
# ~\AppData\Local\Temp/ipykernel_632/688785654.py in <module>
#       1 try:
# ----> 2     a = 1 / 0
#       3     print(a)
#       4 except TypeError as err:
#       5     print(err)
# 
# ZeroDivisionError: division by zero

try で発生したエラーと except で指定しているエラーは異なっているので,エラーが送出されています.
また,異常終了しているので else が処理されず,finally は処理されています.

try のエラーが予想できない場合は,Exception を except のエラーの指定とすると,全てのエラーに対処できます.

try:
    a = 1 / 0
    print(a)
except Exception as err:
    print(err)
else:
    print("else")
finally:
    print("finally")

# division by zero
# finally

以上が条件分岐/繰り返し処理/例外処理についての紹介でしたが,せっかくなので,これらを用いた処理を演習ベースで考え,プログラミングしてみましょう.

演習問題

  • Q.1: 1 から 1000 までの 5 の倍数の総和を求めてください.できる人は for を使わず 1 行で挑戦してみてください.
  • Q.2: 1 から 1000 まで連続した整数の内,7 と 13 の倍数に 1 足した値のみを加算してください(% を用います).
  • Q.3: 0 から 100 まで連続した整数の2乗根を要素とする,要素数 101 の次のリスト \([\sqrt{0},\sqrt{1},\sqrt{2},⋯,\sqrt{100}]\) を作成してください(.append() を用います).
  • Q.4: 1 から 100 まで連続した 100 個の整数に対する標準偏差を求めてください.ここで,データの個数を \(n\), i番目のデータを \(x_i\), データの平均を \(\bar{x}\) とすると,標準偏差 \(\sigma\) は,

$$
\sigma = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (x_i – \bar{x}})^2
$$

  で計算されます.

  • Q.5: try-except を用いて,次のリスト [1, 2, 3, “a”] の総和を取った時,”数値と文字列の総和は不可” と表示するように作成しましょう(if でもできますが,try-except を用いてください).

演習問題の解答

  • Q.1: 1 から 1000 までの 5 の倍数の総和を求めてください.できる人は for を使わず 1 行で挑戦してみてください.
val = 0
for i in range(5,1001,5):
    val += i
val

# 100500

for を使わず 1 行で書く方法として(内包表記も for を使うので除外),

sum(list(range(5,1001,5)))

# 100500
  • Q.2: 1 から 1000 まで連続した整数の内,7 と 13 の倍数に 1 足した値のみを加算してください(% を用います).
ret = 0
for i in range(1, 1001, 1):
    if (i % 7 == 1) or (i % 13 == 1):
        ret += i
ret

# 104313
  • Q.3: 0 から 100 まで連続した整数の2乗根を要素とする,要素数 101 の次のリスト \([\sqrt{0},\sqrt{1},\sqrt{2},⋯,\sqrt{100}]\) を作成してください(.append() を用います).
ret = []
for i in range(101):
    ret.append(i**(1/2))
len(ret), ret[-1]

# (101, 10.0)
  • Q.4: 1 から 100 まで連続した 100 個の整数に対する標準偏差を求めてください.
x = list(range(1,101,1))
xbar = sum(x) / len(x)
mse = 0
for i in range(len(x)):
    mse += (x[i] - xbar)**2
sigma = (mse / len(x)) ** (1/2)
sigma

# 28.86607004772212
  • Q.5: try-except を用いて,次のリスト [1, 2, 3, “a”] の総和を取った時,”数値と文字列の総和は不可” と表示するように作成しましょう(if でもできますが,try-except を用いてください).
arr = [1, 2, 3, "a"]
try:
    arr_sum = sum(arr)
except Exception:
    print("数値と文字列の総和は不可")

# 数値と文字列の総和は不可

Python のおすすめの学習方法

プログラミングを最短で習得する,少なくても自分の意志で使えるようになる方法について,いくつかプログラミング言語を触ってきた筆者としては何の言語においても,以下2点が重要だと思います.

  • 元々自分が他の言語で作っていた処理を違う言語で書き直す・・・・英語を勉強するときも,脳を生まれたばかりのまっさらな状態から勉強するわけではなく,日本語を通したり対比して,学習済みの言語野を用いて勉強するのと似ています
  • 言語自体を網羅的に勉強するのではなく,やりたい事を先に考え,それを達成するために色々と調べながら実装する・・・・例えば,留学で語学力が上達するのは,その国の言葉を使ってコミュニケーションを取ることが強制されるためであり,使うことに対するモチベーションが一番大事です

独学で行うには,やはり2点目の「やりたい事ドリブン学習」が効果的で,例えば次の書籍は,Python を流行らせている AI/データ分析/機械学習/深層学習について実装することに主眼を置き説明されているので,実際に手を動かしながら学んでいける本だと思います(筆者も最初にこちらの書籍で遊びながら学びました).

コメント

タイトルとURLをコピーしました