Python/条件分岐と繰り返し

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

Python の制御構造には、逐次・分岐・繰返しの3つがあり。このうち逐次はプログラムが左上から左から右・上から下に進む当たり前の状態ですので、これ以上の説明は必要ないと思います[1]

条件分岐[編集]

条件分岐とは、ある条件を満たすかどうかで、次に実行するプログラムの位置を変えることです。

if文[編集]

最も素朴な条件分岐は、単一のif文です[2]

if文による条件分岐
x = 3

if x == 2 :
    print("abcd")
条件分岐の上のコードの、x == 2とは、「xは2に等しい」という意味です。
冒頭の「x = 3」は、xに3を代入するという意味です。
python では、if文にある条件式にある等号と、代入との等号を区別するために、条件分岐での等号にはx == 2を使います。
上のコードでは、「xは2に等しい」の条件を満たさないため(xには3が代入されている)、実行しても文「abcd」は表示されません。
また、pythonでは、if文の中の文(ブロックと言います)は、字下げをします(この様なインデントによる構造構文をオフサイドルールと言います)。
インデントは半角スペース4つが推奨されます。
実行結果

※なにも表示されない。


つぎに、if文のなかのxの条件を変えてみて、条件が成り立つようにしましょう。

条件が真になるように変更
x = 3

if x == 3 :
    print("abcd")
上のコードでは、「xは3に等しい」の条件を満たすので、実行したら文「abcd」が表示されます。
実行結果
abcd

つまり、if文の構文は、次のようになります。

if 条件式 :
    処理
if文のブロックの外に文を追加
x = 3

if x == 2 :
    print("abcd")
print("ef")

上のコードでは、print("ef")はif文のブロックの外にあることになります。 なぜなら、print("ef")は字下げされてないからです。 なので、プログラムを実行すると「ef」が表示されます。

いっぽう、次のコードでは、結果が変わります。

if文のブロックに文を追加
x = 3

if x == 2 :
    print("abcd")
    print("ef")

上のコードでは、print("ef")はif文ブロックの中にあるので、プログラムを実行しても、「ef」は表示されませんし、もちろん「abcd」も表示されません。

ifブロック内は、字下げをそろえないと、エラーになります。

インデントに誤りのある例
x = 3

if x == 2 :
    print("abcd")
   print("ef")

(↑エラーになる。)

下記コードでは、x==2が満たされていないので、インデントされたブロックは実行されない。そのため実行すると「プログラムが終了しました。」だけが表示される。

x = 3

if x == 2 :
    print("abcd")
    print("ef")
print("プログラムが終了しました。")

条件式には、不等号(><など)も使える。

x = 3

if x > 2 :
    print("xは2よりも大きい。")
print("プログラムが終了しました。")

上のコードを実行すると「xは2よりも大きいよ。」と「プログラムが終了しました。」が表示される。

比較演算子[編集]

記号>のような、値を比較する演算子のことを比較演算子といいます。 pythonで使える比較演算子には、次の表のようなものがあります。

比較演算子
演算子 意味
> 左側の数が、右側の数よりも大きい
< 左側の数が、右側の数よりも小さい
== 両辺の数値の大きさが等しい
!= 等しくない
>= 左側の数が、右側の数よりも大きいか、
または両辺が等しい
<= 左側の数が、右側の数よりも小さいか、
または両辺が等しい

in 演算子[編集]

in 演算子は、オブジェクトがコレクションの要素にあるかを返します。

in 演算子
li = [2,3,5]
tu = (10,20,30)

print(2 in li)
print(2 in tu)
実行結果
True
False

else節のあるif文[編集]

前の節では、if文の条件式によってブロックの実行/否を切り替えました。 ここでは、一歩進めて条件式に合致していなかった場合に別のブロックを実行するelse節を含んだ条件実行構文を紹介します[3]

else節のあるif文
x = 3

if x == 2 :
    print("xは2に等しい。")
else :
    print("xは2に等しくない。")
print("プログラムが終了しました。")
実行結果
xは2に等しくないよ。
プログラムが終了しました。

なお、elseをつかわないでも、同じ内容の処理が書けます。

else節を使わず、逆論理の別のif文を使った例
x = 3

if x == 2 :
    print("xは2に等しい。")
if x != 2 :
    print("xは2に等しくない。")
print("プログラムが終了しました。")
2つの例は同じ動作をしますが、後者は「変更漏れの原因となる」という重大な欠点があるので、好んで逆論理のif文を使わずelseを使いましょ。

elif節のあるif文[編集]

else節を使うと、条件式によって2方向にプログラムフローを分けることができました。 実施のプログラムでは、2つ以上の条件式によってプログラムフローを分けることがあります。 そのような場合、次のように条件式ごとにif文が入れ子になります。

else節のあるif文
x = float("nan")

if x < 0 :
    print("xは0より小さい。")
else :
    if x > 0 :
        print("xは0より大きい。")
    else :
        if x == 0 :
            print("xは0と等しい。")
        else :
            print("xは", x)
print("プログラムが終了しました。")
実行結果
xはnan 
プログラムが終了しました。

このプログラムは意味的には正しいのですがインデントが深くなりすぎ、読むのも修正するのも難儀なので、Python にはelse節の中にif文が入れ子になっていことを表すキーワード elif が用意されています。

elifによる置換え
x = float("nan")

if x < 0 :
    print("xは0より小さい。")
elif x > 0 :
    print("xは0より大きい。")
elif x == 0 :
    print("xは0と等しい。")
else :
    print("xは", x)
print("プログラムが終了しました。")
実行結果
xはnan 
プログラムが終了しました。
インデントが深くなりすぎず読みやすいですね。

if 式[編集]

Python には、if文の他、if式があります。

コード例
x = 3

print("xは2に等しい。" if x == 2 else "xは2に等しくない。")
print("プログラムが終了しました。")
これで、else の例と同じ意味になります。
PerlRubyの中置のifと似ていますが else を伴うので、C言語系の条件演算子と(構文はまるで違いますが)機能的には同じです。
大概の場合、if文を使ったほうが読みやすいのですが、内包表記やラムダ式では式が要求されるので、if式を使う必要があります。

つまり、上記コードの実行結果は下記のようになります。

実行結果
xは2に等しくない。
プログラムが終了しました。
nanってナニ?
唐突に float("nan")、が出てきましたが、これがPythonでNaN(Not a Number; 非数)を得る方法で、他には math を import して math.nan を使う方法もあります。

nan は、浮動小数点演算の結果として、不正なオペランドを与えられたために生じた結果を表す値またはシンボルで[4]、NaNの体系的仕様は、無限大の表現などと共に1985年の IEEE 754 浮動小数点規格で標準が与えられています。

nanの特徴
どんな値と比較しても不一致
nan 自身とも一致しない( nan == nan ⇒ False )
どんな数学関数の引数に与えても結果は nan
nan であるかは is 演算子をつかう( nan is nan ⇒ True)
あるいは math を import し math.isnan(x) をつかう( isnan(nan) ⇒ True)


論理演算子[編集]

「変数xは2より大きくて、5より小さい」ときにだけ実行するような処理は、次のように書きます。

x = 3

if x > 2 and x < 5 :
    print("xは2より大きくて5より小さい。")
else:
    print("そうでない場合。")
print("プログラムが終了しました。")

andは、両方の条件が成り立つという意味です。

上記のコードを実行すると、「

xは2より大きくて5より小さいよ。

」が表示されます。「そうでない場合。」は表示されません。


xを6に変えてみましょう。

x = 6

if x > 2 and x < 5 :
    print("xは2より大きくて5より小さい。")
else:
    print("そうでない場合。")
print("プログラムが終了しました。")

上記のコードを実行すると、「

そうでない場合。

」が表示されます。「xは2より大きくて5より小さいよ。」は表示されません。

演算子 andをもちいて、

if 条件式A and 条件式B :

のように、使いうことにより、条件式Aと条件式Bが両方とも成り立つ場合に、「条件式A and 条件式B」が成り立つという意味にできます(数理論理学の用語でいう「論理積」です)。

論理演算子
演算子 意味
and 両方とも真。(「論理積」)
or 片方または両方が真。(「論理和」)
not 真ならば偽・偽ならば真。(「論理和」)
複数の比較演算子の連結

上記の例では、and 演算子の説明のため

  • x > 2 and x < 5

としていますが、これは

  • 2 < x < 5

と書くこともできます。両者は同じ意味になりますがxの参照は後者は一度だけになります。


match文[編集]

Pythonには、C言語のswitch文はありませんでしたが、Python 3.10 から match 文がサポートされました[5]

match文を使った条件分岐
for x in range(10):
    print(x,"=", end=" ")
    match x:
        case 2:
            print('two')
        case 5:
            print('five')
        case 7:
            print('seven')
        case _:
            print('ELSE')
実行結果
0 = ELSE
1 = ELSE
2 = two
3 = ELSE
4 = ELSE
5 = five
6 = ELSE
7 = seven
8 = ELSE
9 = ELSE
switch文のdefault節に相当する機能は、case節 の値を _ にすることによって実現します。

match文は、2021年に導入された新しい機能なので、3.10 より古い3.9以下バージョンのpythonではmatch文は使えません。なので古いバージョンでは、if文などで代用してください。

連想配列を使った多方向分岐[編集]

上の例は、連想配列を使って下のように書き換えることができます。

連想配列を使った例
h = { 2: "two", 5: "five", 7: "seven"}

for x in range(10) :
    print(f"{x} =", x in h and h[x] or "ELSE")

列挙型[編集]

python では モジュール Enum で列挙型が提供されています[6]。 Enum はclassを用いる方法と、Enum関数を用いる方法の2つがありますが、ここでは class を用いる方法を紹介します。

列挙型
from enum import Enum

class GameMode(Enum):
    map = 1
    menu = 2
    battle = 3

currentMode = GameMode.menu

assert currentMode in GameMode
if currentMode == GameMode.map:
    print('マップ画面を探検中')
elif currentMode == GameMode.menu:
    print('キャンプを開いて体制を整えよう')
elif currentMode == GameMode.battle:
    print('敵襲!戦闘開始だ!')
実行結果
キャンプを開いて体制を整えよう

map = 1 などのブロックの値は、べつに大小が順番どおりである必要はなく、それどころかWindowsで実験した限りは整数値である必要すらもなく小数値や数式ですら代入できてしまうのですが、しかしわざわざ分かりづらい数値を与える必要もないし、今後のバージョンの仕様変更なども想定して、なるべく普通の整数値を与えるのが安全だと思います。


なお、pythonの場合、enum変数をprint出力すると、下記のように格納された変数名(例の場合では"GameMode.menu")が出力されます。

enum変数自体には、「<GameMode.menu: 2>」のように変数名と値の両方のペアが格納されます。

それだけではenum変数を数値演算に活用できず不便なので、enum変数の数値を指定したい場合は下記のように.valueプロパティを指定します。

from enum import Enum

class GameMode(Enum):
    map = 1
    menu = 2
    battle = 3

currentMode = GameMode.menu

assert currentMode in GameMode
if currentMode == GameMode.map:
    print('マップ画面を探検中')
elif currentMode == GameMode.menu:
    print('キャンプを開いて体制を整えよう')
elif currentMode == GameMode.battle:
    print('敵襲!戦闘開始だ!')
    
print(currentMode)

print(repr(currentMode))

print(currentMode.value)

print(currentMode.name)
実行結果
キャンプを開いて体制を整えよう
GameMode.menu
<GameMode.menu: 2>
2
menu

.nameプロパティは上記のように、GameMode.menu ではなく menu の部分だけを指定します。

他のプログラミング言語では、enum変数のprint表示の形式は違うかもしれないので、それぞれの言語のマニュアルを参照してください。

Enum.auto()の使用[編集]

値をきめるのが面倒な場合、auto をインポートすれば、pythonが自動的にきめてくれます。

Enum.auto()の使用
from enum import Enum, auto

class GameMode(Enum):
    map = auto()
    menu = auto()
    battle = auto()

currentMode = GameMode.map

assert currentMode in GameMode
if currentMode == GameMode.map:
    print('マップ画面を探検中')
elif currentMode == GameMode.menu:
    print('キャンプを開いて体制を整えよう')
elif currentMode == GameMode.battle:
    print('敵襲!戦闘開始だ!')

print(currentMode.value)
実行結果
マップ画面を探検中
1

__str__を定義[編集]

メソッド __str__ を定義すると、str() をオーバーロードできます。

__str__を定義
from enum import Enum, auto

class GameMode(Enum):
    map = auto()
    menu = auto()
    battle = auto()
    def __str__(self) :
        labels = {
            self.map: "マップ画面を探検中",
            self.menu:"キャンプを開いて体制を整えよう",
            self.battle: "敵襲!戦闘開始だ!" }
        return labels[self]

currentMode = GameMode.battle

print(currentMode)
実行結果
敵襲!戦闘開始だ!

別解[編集]

列挙子の値は整数でなくてもよく、autoプロシージャを使うまでもなくメッセージ文字列そのものを値とし、値には self.value でアクセスしました。 併せて、クラスメソッドの実装例も追加しました。

別解
from enum import Enum

class GameMode(Enum):
    map =  "マップ画面を探検中"
    menu = "キャンプを開いて体制を整えよう"
    battle = "敵襲!戦闘開始だ!"
    def __str__(self) :
        return self.value

    @classmethod
    def names(cls):
        return [e.name for e in cls]

    @classmethod
    def values(cls):
        return [e.value for e in cls]

    @classmethod
    def hash(cls):
        return {e.name: e.value for e in cls}

currentMode = GameMode.menu

print(currentMode)
print(GameMode.names())
print(GameMode.values())
print(GameMode.hash())
実行結果
キャンプを開いて体制を整えよう
['map', 'menu', 'battle']
['マップ画面を探検中', 'キャンプを開いて体制を整えよう', '敵襲!戦闘開始だ!'] 
{'map': 'マップ画面を探検中', 'menu': 'キャンプを開いて体制を整えよう', 'battle': '敵襲!戦闘開始だ!'}

繰返し[編集]

繰返しに関するPythonの構文には、while文for文の2つがあります。

while文[編集]

while 文は、ある条件を満たすあいだ、処理を繰返します。

構文
while 条件式 : 
    処理
コード例
i = "b"
while i != "q":
    i = input("入力した文字を3個、ならべるよ。(終了したい場合はqを入力)")
    print(3*i)
条件式は自動的に論理型に変換されます。
上記のプログラムを実行すると、まず「入力した文字を3個、ならべるよ。(終了したい場合はqを入力)」と言われる。
入力文字として、たとえば「a」と入力すると、「aaa」と出力表示され、そして再び「入力した文字を3個、ならべるよ。(終了したい場合はqを入力)」と聞かれ、ふたたび入力を待つ状態になる。なので、「b」と入力してみると、「bbb」と出力表示される。
上記のプログラムは、文字「q」が入力されないかぎり、動作がつづく。文字「q」を入力することにより、終了する。

代入演算子[編集]

上記の while 文の例は、代入演算子をつかうと、

コード例(悪い例)
while (i := input("入力した文字を3個、ならべるよ。(終了したい場合はqを入力)")) != 'q' :
    print(3*i)
のように書換えることができる。

while文とif文との組合わせ[編集]

while文はif文と組合わせることもできる。

while文とif文との組合わせ(悪い例)
while (i := input("文字を入力しよう。sを入力で物語がスタート。(終了したい場合はqを入力)")) != 'q' :
    if i == "s":
        print("はじまりはじまり。")
        print("むかし、むかし、あるところに、")
        print("おじいさんとおばあさんが、すんでいました。")
        print("おしまい")
上記のプログラムを実行すると、文字「s」を入力することで「はじまりはじまり。」と表示させることができ、そして「むかし、むかし、あるところに、」「おじいさんとおばあさんが、すんでいました。」「おしまい」と表示する。
sを入力しなければ、「はじまりはじまり。」などは表示されない。
このコードは、while の条件式に入力のロジックと終了条件のロジックを合成して読解と修正を困難にしている悪い例です。#break文に改善した例があります。

for文[編集]

素朴なfor文
for i in range(5):
    print("a")
のように書いて実行すると、組込み関数range()のカッコ内の回数だけ、ブロック内の文を繰り返す。上のコードの場合、実行すると、「a」を5回、表示する。
実行結果
a
a
a
a
a

forのあとの変数(ループ変数と呼ばれます)は、別にiである必要はなく、特に以後値を参照しない場合は _ を使うと慣習的に「参照しないこと」が明示できます。

ループ変数が以後参照しないことを明示している例
for _ in range(5):
    print("a")

ただし、あくまで慣習にすぎず、言語仕様上は _ という名前の変数にすぎないので、_ の値を参照するプログラムを書けば実行でき、インタプリターは何の警告もしません。 また、pylint の様な静的なソースコード診断ツールでは、_ は未使用変数のチェックの対象から外されているなど、言語仕様ではないものの _ を特別な識別子として扱う事は慣例的に定着しているので、_ の値を参照するコードを書かないことを強く推奨します。

慣習上、for文を書くときは、i で回数を数えることが多い[7]

for i in range(5):
    print(i)

のように、すると、

実行結果
0
1
2
3
4

のように表示されます。 組込み関数 range() は range クラスのオブジェクトを返します。

range以外のオブジェクトとfor文の組み合わせ[編集]

range以外のオブジェクトとfor文の組み合わせ
li = [2, 3, 5]
tu = (10, 20, 30)

for i in li:
    print(i, end=", ")
else:
    print()

for i in tu:
    print(i, end=", ")
else:
    print()

for i, j in zip(li, tu):
    print(i, j, sep=":", end=", ")
else:
    print()

print(type(zip(li, tu)))
実行結果
2, 3, 5, 
10, 20, 30, 
2:10, 3:20, 5:30, 
<class 'zip'>

continue文[編集]

continue文は、ループの先頭に戻ります。continue文を含むループのブロックのcontinue文以降は実行されません。

pass文[編集]

pass文は、なにもしません。 「なにもしないが構文上ブロックが要求される」ケースで使われます。

break文[編集]

while文あるいはfor文のブロックで文「break」を使うと、ループを中断します。

i = "b"
while True:
    i = input("文字を入力しよう。sを入力で物語がスタート。(終了したい場合はqを入力))
    if i == "s":
        print("はじまりはじまり。")
        print("むかし、むかし、あるところに、")
        print("おじいさんとおばあさんが、すんでいました。")
        print("おしまい")
    elif i == "q":
        break

break文で中断する対象は、if文ではない。break文は、ループを中断するものです。

なおwhile Trueのように、while直後の条件式をブール値 True にすると、条件式は常に真となり、永久ループとなります[8]

ループにつづくelse節[編集]

while文あるいはfor文がbreak文で中断しなかった場合forにつづくelse節が実行されます。else節は、大概のプログラミング言語ではif文と組合わせて使いますが、Pythonではこれに加え繰返し(while文あるいはfor文)と組合わせることができます。

while+else[編集]

while+else
i = 0
while i < 100:
    print(i, end=" ")
    i += 3
else:
    print("done!")

i = 0
while i < 100:
    if i > 10:
        break
    print(i, end=" ")
    i += 3
else:
    print("done!")
実行結果
0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 done!
0 3 6 9
ループをカンストした場合は else 節を実行し、break (や return)で中断すると else 節は実行されません。

for+else[編集]

for+else
h = {2: "two", 5: "five", 7: "seven"}

for x in list(h):
    print(h[x])
else:
    print("I didn't take a break.")

for x, v in h.items():
    if x == 7:
        break
    print(x, v)
else:
    print("I didn't take a break.")
実行結果
two
five
seven
I didn't take a break.
2 two
5 five
ループをカンストした場合は else 節を実行し、break (や return)で中断すると else 節は実行されません。
ループとelseを組合わせた大域脱出
Pythonはgoto分やラベル付きbreak文を持っていないので、大域脱出する場合は
  • ループ全体を関数にしてreturn文で脱出
  • tryをループをくくり例外をあげて脱出
  • プログラム自身を終了

などの工夫が必要です。

ここでは、ループと結合したelseを使った大域脱出を紹介します。

ループと結合したelseを使った大域脱出
for i in range(10):
    for j in range(10):
        for k in range(10):
            if (i == 2 and j == 3 and k == 5):
                break
            print(f"{i}{j}{k}", end=" ")
        else:
            print()
            continue
        break
    else:
        continue
    break
else:
    print("done!")
実行結果
000 001 002 003 004 005 006 007 008 009 
010 011 012 013 014 015 016 017 018 019 
020 021 022 023 024 025 026 027 028 029 
030 031 032 033 034 035 036 037 038 039 
040 041 042 043 044 045 046 047 048 049 
050 051 052 053 054 055 056 057 058 059 
060 061 062 063 064 065 066 067 068 069 
070 071 072 073 074 075 076 077 078 079 
080 081 082 083 084 085 086 087 088 089 
090 091 092 093 094 095 096 097 098 099 
100 101 102 103 104 105 106 107 108 109 
110 111 112 113 114 115 116 117 118 119 
120 121 122 123 124 125 126 127 128 129 
130 131 132 133 134 135 136 137 138 139 
140 141 142 143 144 145 146 147 148 149 
150 151 152 153 154 155 156 157 158 159 
160 161 162 163 164 165 166 167 168 169 
170 171 172 173 174 175 176 177 178 179 
180 181 182 183 184 185 186 187 188 189 
190 191 192 193 194 195 196 197 198 199 
200 201 202 203 204 205 206 207 208 209 
210 211 212 213 214 215 216 217 218 219 
220 221 222 223 224 225 226 227 228 229 
230 231 232 233 234
最外周のprint("done!")は(breakされた場合は)実行されません。


ループはスコープを作りません[編集]

Pythonでは、ループはスコープを作りません。

ループはスコープを作りません
i = "abc"
j = "xyz"
print(f"forの前: {i=}{j=}")
for i in range(5):
    j = 2 * i
    print(f"forの中: {i=}{j=}")
print(f"forの後: {i=}{j=}")
実行結果
forの前: i='abc'、j='xyz'
forの中: i=0、j=0
forの中: i=1、j=2
forの中: i=2、j=4
forの中: i=3、j=6
forの中: i=4、j=8 
forの後: i=4、j=8

脚註[編集]

  1. ^ 関数やメソッドの呼出しや復帰 return も制御構造と言えますが、本項では逐次・分岐・繰返しについて述べます。
  2. ^ 4.1. if Statements” (2021年11月17日). 2021年11月18日閲覧。
  3. ^ else節は、他の言語と違いfor文とも結合しますので注意してください。
  4. ^ ただし、Pythonでは math.asin(2) ↑ValueError であったりnanを返す前に例外が上がる事が多々ある。
  5. ^ 4.6. match Statements” (2021年11月17日). 2021年11月18日閲覧。
  6. ^ enum — Support for enumerations” (2021年11月17日). 2021年11月18日閲覧。
  7. ^ 少なくともFortranではdo文のループ変数に i が常用され、それ以前に数学でも数列の和など i が使用される。由来は、iteration の i; integer の i など諸説あります。
  8. ^ while 1:としても無限ループになりますが、これは整数型が論理型に変換されるとき「0はFalse, 0以外はTrue」に暗黙に変換されることに頼っています(意味を明示するためwhile True:とすべき)。同様にwhile i:として、ループ変数 i(たぶん整数型の値)がセロになるまで繰り返すのようなコードも暗黙変換に頼っています。このようなコードは小さな変更で最初の意図に反した挙動を示します。たとえば i-=1 を i-=2 にした場合、i=1の次はi=-1となり、ループ終了条件のi=0を経ることなく無限ループになります。この場合は while i > 0:とすべきです。