今どきのPythonプログラミング

プログラマーの尾関です。

個人的にPytyon2.7あたりの知識で止まっているので、勉強も兼ねて Python3から導入された「Enumクラス」「型ヒント」「match-case文」についてまとめてみました。

1. Enumクラス

Enumクラスは C++ や C# にある enum キーワードを Python 用に拡張したクラスです。

Information

Enumクラスは、Python 3.4 から使用可能となりました。

概要

C++言語などを使用していれば当たり前のことですが、Enumクラスを使うと以下のメリットがあります。

  • 明確な状態を表す定数を定義しやすくなる
  • 複数の定数をグループ化して管理しやすくなる
  • 型安全性の向上、エラー防止につながる

基本的な使い方

使い方として、Enumクラスを import し、それを継承したクラスで実装します。

# Enumクラスを使う.
from enum import Enum

# Enumクラスの定義.
class Direction(Enum):
    NONE = 0
    LEFT = 1
    UP = 2
    RIGHT = 3
    DOWN = 4

テストコードは以下の通りです。

# Enumクラスを使う.
from enum import Enum

# Enumクラスの定義.
class Direction(Enum):
    NONE = 0
    LEFT = 1
    UP = 2
    RIGHT = 3
    DOWN = 4

# 使用例.
def main():
    print(Direction.LEFT) # -> "Direction.LEFT"
    print(Direction.LEFT.name) # 定義名を取れる -> "LEFT"
    print(Direction.LEFT.value) # 値を取れる ->   1
    print(Direction.LEFT == Direction.LEFT) # 一致するので True
    print(Direction.LEFT == Direction.RIGHT) # 一致しないので False
    print(Direction.LEFT == 1) #-> 型が異なるので False
    print(Direction.LEFT == "LEFT") #-> 型が異なるので False

if __name__ == "__main__":
    main()

ただC++言語のように、enumと数値はそのままでは比較できず、型の比較を厳密に行う必要があります。

IntEnum (数値と比較可能なEnum)

もしC++言語のように整数値と比較可能な enumを使いたい場合には IntEnum クラスを使用します。

# -*- coding: utf-8 -*-
# 数値型Enumクラスを使う.
from enum import IntEnum

# Enumクラスの定義.
class Direction(IntEnum):
    NONE = 0
    LEFT = 1
    UP = 2
    RIGHT = 3
    DOWN = 4

# 使用例.
def main():
    print(Direction.LEFT) # IntEnumは数値となる -> Direction.LEFT
    print(Direction.LEFT.name) # 定義名を取れる -> "LEFT"
    print(Direction.LEFT.value) # 値を取れる -> 1
    print(Direction.LEFT == Direction.LEFT) # 一致するので True
    print(Direction.LEFT == Direction.RIGHT) # 一致しないので False
    print(Direction.LEFT == 1) # 数値型と比較可能 -> True
    print(Direction.LEFT == "LEFT") # -> 型が異なるので False

if __name__ == "__main__":
    main()

直接数値と比較できるようになりました。データ仕様の都合などで数値と比較したいときに便利です。

数値をIntEnumに変換する

整数値を IntEnum として扱いしたい場合には、IntEnumのコンストラクタに数値を渡すことで変換可能です。

# -*- coding: utf-8 -*-
# 数値型Enumクラスを使う.
from enum import IntEnum

# Enumクラスの定義.
class Direction(IntEnum):
    NONE = 0
    LEFT = 1
    UP = 2
    RIGHT = 3
    DOWN = 4

# 使用例.
def main():
    # 数値をIntEnumに変換する例.
    num = 2
    try:
        direction = Direction(num)
        # 変換可能.
        print(f"数値 {num} は {direction.name} に対応します。")
    except ValueError:
        # 変換できない場合.
        print(f"数値 {num} は有効な方向ではありません。")

if __name__ == "__main__":
    main()

Enumクラスにはメンバ関数が定義できる

PythonのEnumはクラスであることから、メンバ関数を定義できます。

例えば方向のEnmuであれば、自身が水平方向か垂直方向かどうかを判定する関数を定義できます。

# -*- coding: utf-8 -*-
# 数値型Enumクラスを使う.
from enum import IntEnum

# Enumクラスの定義.
class Direction(IntEnum):
    NONE = 0
    LEFT = 1
    UP = 2
    RIGHT = 3
    DOWN = 4
    def is_horizontal(self):
        """ 水平方向かどうか"""
        return self in (Direction.LEFT, Direction.RIGHT)

    def is_vertical(self):
        """ 垂直方向かどうか"""
        return self in (Direction.UP, Direction.DOWN)

# 使用例.
def main():
    direction = Direction.LEFT # 左.
    print(f"Direction: {direction.name}")
    print(f"- 水平方向? {direction.is_horizontal()}") # -> True
    print(f"- 垂直方向? {direction.is_vertical()}") # => False

    direction = Direction.UP # 上.
    print(f"\nDirection: {direction.name}")
    print(f"- 水平方向? {direction.is_horizontal()}") # -> False
    print(f"- 垂直方向? {direction.is_vertical()}") # => True

if __name__ == "__main__":
    main()

PythonのEnumはクラスなので、selfキーワードで自身の値を判定することになります。

2. 型ヒント (Type Hints)

本来 Python は動的型付け言語ですが、厳密な型を定義するための「型ヒント」が導入されました。

Information

型ヒントは Python 3.5 から利用可能となりました

概要

  • 型ヒントによって静的型付け言語に近いメリットが得られる
  • コード補完機能が動作しやすくなって入力が捗る
  • 静的解析によるエラー検出が用意となる

基本的な使い方

# -*- coding: utf-8 -*-
def add_numbers(a: int, b: int) -> int:
    """ 2つの整数を足し算する関数 """
    return a + b


# 使用例.
def main():
    # 使用例
    result = add_numbers(5, 10)
    print(result)  # 出力: 15

    add_numbers(5, "10")  # TypeError: 引数は整数でなければなりません

if __name__ == "__main__":
    main()

「:(コロン)」の後ろにデータ型を指定する記述となります。

これによって、型エラーを検出することができますが、コンパイルエラーではなく実行時エラーとなるので注意が必要です。

具体的には実行時には以下のエラーで停止します。

C:\Users\ozeki\Desktop>python test.py
15
Traceback (most recent call last):
  File "C:\Users\ozeki\Desktop\test.py", line 16, in <module>
    main()
    ~~~~^^
  File "C:\Users\ozeki\Desktop\test.py", line 13, in main
    add_numbers(5, "10")  # TypeError: 引数は整数でなければなりません
    ~~~~~~~~~~~^^^^^^^^^
  File "C:\Users\ozeki\Desktop\test.py", line 4, in add_numbers
    return a + b
           ~~^~~
TypeError: unsupported operand type(s) for +: 'int' and 'str'

静的解析を有効にする方法

VSCodeで静的解析を有効にするには「Pyright」などをインストール。

この拡張機能をインストールすると、例えば数値型の引数に文字列を指定した場合、実行しなくてもエラーメッセージが表示されるようになります。

3. match-case文

Pythonは長らく switch-case文を書くことができませんでしたが、match-case文という形で書くことができるようになりました。

Information

match-case文は Python 3.10 から利用可能となりました

概要

  • 従来の if-elsif-else文よりも可読性が高い記述が可能となった
  • さらに「パターンマッチング」により複雑な判定が簡潔な記述で行える

パターンマッチング

Pythonの match-case文は、単なる switch-case文ではなく、高度なパターンマッチング機能を提供しています。

例えば「|」を使って or条件の分岐ができます。

day = input("曜日を入力してください: ").capitalize()

match day:
    case "Saturday" | "Sunday": # 土曜日・日曜日
        print(f"{day} は週末です.")
    case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday": # 月・火・水・木・金.
        print(f"{day} は平日です.")
    case _: # それ以外.
        print("不正な日付.")

また「リスト」のマッチングと変数へのバインドも可能です。

def analyze_list(items):
    match items:
        case []:
            print("空のリストです.")
        case [x]: # 要素数が1つのみ。値を "x" にバインド
            print(f"要素数1のリスト: {x}")
        case [x, y]: # 要素数が2つ。値を "x", "y" にバインド
            print(f"要素数2のリスト: {x}, {y}")
        case [x, y, *rest]: # 要素を "x", "y" にバインド。さらに それ以降の要素を rest にすべてバインド.
            print(f"0番目:{x}, 1番目:{y}, 残りは {len(rest)} つの要素があります.")

さらにユーザー定義オブジェクトのマッチングも可能です。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

point = Point(3, 4)

match point:
    case Point(x=0, y=0): # Pointのメンバ変数をまとめて比較.
        print("(x, y) = (0, 0) です")
    case Point(x=x, y=0): # point.y = 0 であれば、point.x を変数 "x" にバインド.
        print(f"Y軸並行 x={x}")
    case Point(x=0, y=y): # point.x = 0 であれば、point.y を変数 "y" にバインド.
        print(f"X軸並行 y={y}")
    case Point(x=x, y=y): # point.x を "x"、point.y を "y" にバインド.
        print(f"(x, y) = ({x}, {y}) です")

おしまい

Python 3時代に重要と思われる機能について紹介しました。

特に Enum クラスと match-case文は個人的に見慣れない書き方だったので、とても勉強になりました。

以上、Pythonを書くときのお役に立てれば何よりです。

\ 最新情報をチェック /