制御構文

ifやforに代表される構文です。そんなに変わった構文はないので、理解はすぐに出来ると思います。

ここで学習すること

説明

if

まずはif-elif-else構文から。どのプログラミング言語でも見かけますし、余り説明の余地はありませんが、ifのみ、if-else、if-elif-elseの3パターンがあり得ます。Pythonにはswitch-case文が用意されていないので基本的にはif-elif-elseを使っていくことになります。

サンプル

#!/usr/bin/env python3.8

import sys

argv = sys.argv

if len(argv) == 1:
    print("no arguments")
elif len(argv) == 2:
    print("argument is one!")
else:
    print("arguments are two or more")

実行

$ python if.py 
no arguments
$ python if.py 1
argument is one!
$ python if.py 1 2
arguments are two or more
$ 

なお、開発中に条件分岐だけ構えて内部の処理は後回しにすることもあり得るかと思いますが、ブロック内に処理が無いとPythonでは怒られちゃいます。これはifに限った話ではありませんが、そんなときはpassと一言書いておくとこの指摘から逃れられます。

if True:
    pass

コメントで代用したい所ではありますが、コメントはプログラム上はなき者として扱われるのでダメです。ただ”””TODO なんとかんとか”””、これはプログラム上はコメントではなくて文字列単体の式なので逃れられます。(すいません、完全に脱線です)

while

続いてwhileです。条件式がTrueである限り繰り返し処理が実行されます。なおwhile文にもelseがあります。ループの最後に処理が実行されるので、if文のelseとは少し異なります。すこし動きを知っておく必要があるので、後ほどもう一度触れます。

サンプル

while01.py

#!/usr/bin/env python3.8

n = 0
while n < 5:
    print(n)
    n += 1
else:
    print("end of while")

実行

$ python while.py 
0
1
2
3
4
end of while
$ 

無事にelse含めて動作することが確認できました。

さて、elseについて再び触れておきますと、while文に限らずループ処理には、特定の条件を満たすとそこでループを中断するということがあります。その実装の善し悪しはともかく、とにかくそのように書きたくなることがあります。その場合にはelseは処理されません。

while02.py

#!/usr/bin/env python3.8

n = 0
while n < 5:
    print(n)
    if n == 3:
        break
    n += 1
else:
    print("end of while")

if n == 3:
    print("break the while-loop")
else:
    print("complete the while-loop")

実行

$ python while02.py 
0
1
2
3
break the while-loop
$

つまりwhile-elseのelseが有効なのは、ループの条件に反してループを終える時だけです。〜な図実行したい場合には、whileの後に普通に書けば良いので、用途は限定的だと思います。

なお、breakには触れてcontinueには触れませんでした。for構文のネタに回させてもらいますが、もちろんwhile文でも使えます。

for

for文は、シーケンス (文字列、タプルまたはリスト) や、その他の反復可能なオブジェクト (iterable object) 内の要素に渡って反復処理を行うために使われます。堅い表現をしましたが、公式ドキュメントの受け売りです。まぁ、でもそんな難しい話ではありません。

サンプル

for01.py

#!/usr/bin/env python3.8

list = [1, 2, 3, 4, 5]

for val in list:
    print(val)

実行

$ python for01.py 
1
2
3
4
5
$ 

これだけです。今回はリストを使いましたが、同種のものタプルや辞書などでも構いませんが、とにかくfor-inの構文でこのように書くパターンしか有りません。他の言語をご存じの方は、これだけ?という拍子抜けかと思いますが、for ( i = 0; i < 10; i++) {} 的なのはありません。

さて、while文でお預けにしたcontinueです。

サンプル

for02.py

#!/usr/bin/env python3.8

list = [1, 2, 3, 4, 5]

for val in list:
    if val % 2 == 1: continue
    print(val)

実行

$ python for02.py
2
4
$ 

2で割って余りが1になる、つまり奇数の場合にcontinueしていますのでprint()が実行されず次のループに回っているというものです。

try

続いてはtryです。try-except-else-finallyとraiseについて扱います。

try-exceptは、他の言語ではtry-catchとして使われているものです。tryで囲った処理で何かエラーに遭遇した場合に、exceptで捕捉しエラーの事後処理を行うというためのものです。finallyはエラーの有無にかかわらず必ず実行されるので、何らかの後始末などを担うケースが良くあります。最後にelseですが、これまた使いどころがよく分かりませんが、tryで1つも例外が発生しなかった場合に実行されるものです。それってtryの最後の行で良いのでは?という風にも思ったりしますが、それだとそこでエラーになるとexceptに行ってしまうのでそれを避けたいというとでしょうか…

サンプル

try01.py

#!/usr/bin/env python3.8

for val in [1,2,3,0,5]:
    try:
        print(10 / val)
    except Exception as ex:
        print("exception: " + str(ex))
    else:
        print("else of try-except")
    finally:
        print("finally of try-except\n-----")

実行

$ python try01.py 
10.0
else of try-except
finally of try-except
-----
5.0
else of try-except
finally of try-except
-----
3.3333333333333335
else of try-except
finally of try-except
-----
exception: division by zero
finally of try-except
-----
2.0
else of try-except
finally of try-except
-----
$ 

0で割り算してしまったのでそのループだけexceptでエラーのな内容が表示されています。

elseで1つ大事なことはelseの中で問題が起こるとfinallyは実行されません。なのでelseの中でまたtry-exceptを書くのが良いか、冒頭に述べた通りtryの最後に書いた方が良いのか、そこは検討が必要です。

続いてraiseです。raiseは直接的にはtry-exceptと関係がありません。誤解しないでいただきたいのは、セットで使う必要が必ずしもないということです。もちろんtryの中でraiseして、自身のexceptで捕捉するので何の問題もありません。

サンプル

#!/usr/bin/env python3.8

def divide(val):
    if val == 0:
        raise Exception("No ZERO!")
    print(10 / val)

for val in [1,0,3]:
    try:
        divide(val)
    except Exception as ex:
        print("exception: " + str(ex))

実行

$ python try02.py 
10.0
exception: No ZERO!
3.3333333333333335
$ 

この例では関数内ではraiseするだけにして、呼び出し側で捕捉しています。ファイルを開こうとしたら、ファイルがありませんと怒られる時と同じ構図です。

with

余りメジャーな存在でもありませんが、クラスが対応していればオープンしたリソースをブロック内で使ってそのまま放置すれば、勝手にクローズしてくれるものです。Javaにもtry-with-resrouceの構文がありますが役割はそれと一緒だと思います。

サンプル

#!/usr/bin/env python3.8

# withを用いない書き方
f = open("sample.txt")
print(f.read())
f.close()

# withを用いた書き方
with open("sample.txt") as f:
    print(f.read())

$ python with.py 
sample1
sample2
sample3

sample1
sample2
sample3

$ 

見ての通り出来ることは同じで、書き方が違います。open()をwithと一緒にopen()を書くことでclose()を省略することが出来ます。ファイルのオープンとクローズのほか、通信の開始と終了、データベースへのアクセスにおけるオープンとクローズなどが同様に書けると思います。

ただどうですかね?もしclose()忘れあるいはclose()漏れ対策としてwithをとらえるなら、それに頼るレベルだとしたら、start()したらend()が必要だったのを忘れ(知らない)というのももちろんあり得て…うーん、これって両方の書き方できちんと書けると人だけが選べる感じでしょうか。

なお、もちろんwithが使えるクラスを自作することも可能です。これについてはまた改めて。