はじめに
C#を過去に使っていたのですが、最近Pythonで実装することになり、列挙型(Enum)を使おうとしたところ、「あれ?C#と全然違う...」となった経験があります。
C#では当たり前にできていたことが、Pythonだと別のやり方が必要だったり、逆にPythonの方が便利な機能があったり。同じ「Enum」という名前なのに、なぜこんなに違うのか?
この記事では、C#開発者の視点でPythonのEnum事情をまとめてみました。API開発やDB設計で「どっちを使うべき?」と迷った時の参考にしてください。
C#のEnumをおさらい
まずは馴染みのあるC#のEnumから。基本的な特徴を確認しておきましょう。
基本的な使い方
public enum Color : int
{
Red = 1,
Green = 2,
Blue = 3
}
var c = Color.Red;
Console.WriteLine(c); // "Red"
Console.WriteLine((int)c); // 1
ビットフラグ
[Flags]
public enum Permission
{
Read = 1,
Write = 2,
Execute = 4
}
var perm = Permission.Read | Permission.Write;
Console.WriteLine(perm.HasFlag(Permission.Write)); // True
C#のEnumは:
- 既定で
int
ベース(byte
、long
等も指定可能) -
[Flags]
属性でビット演算ができる - JSONシリアライズは数値が既定(設定で文字列化も可能)
- 値との直接比較が簡単
PythonのEnumを使ってみた
基本的なEnum
from enum import Enum, auto
class Color(Enum):
RED = auto()
GREEN = auto()
BLUE = auto()
c = Color.RED
print(c.name) # "RED"
print(c.value) # 1
最初に驚いたのはauto()
の存在。C#だと値を明示的に指定するのが普通ですが、Pythonでは自動採番できるんですね。デフォルトでは1から順番に採番されますが、内部的には generate_next_value で決まるため、カスタマイズも可能です。
IntEnum:整数との比較ができるEnum
from enum import IntEnum
class Status(IntEnum):
PENDING = 1
PROCESSING = 2
COMPLETED = 3
# これができる!
print(Status.PENDING == 1) # True
print(Status.PENDING < Status.COMPLETED) # True
通常のEnum
だとStatus.PENDING == 1
はFalse
になってしまいます。C#に慣れていると、この違いでハマりがちです。
Flag:ビット演算対応のEnum
from enum import Flag, auto
class Permission(Flag):
READ = auto()
WRITE = auto()
EXECUTE = auto()
perm = Permission.READ | Permission.WRITE
print(Permission.WRITE in perm) # True
C#の[Flags]
と同じようなことができます。in
で含まれているかチェックできるのは直感的ですね。
整数との比較も行いたい場合は IntFlag を使うとC#の [Flags] により近い挙動になります。
StrEnum:文字列特化のEnum(Python 3.11+)
from enum import StrEnum
class OrderStatus(StrEnum):
PENDING = "pending"
PROCESSING = "processing"
COMPLETED = "completed"
status = OrderStatus.PENDING
print(status) # "pending"
print(status.value) # "pending"
print(str(status)) # "pending"
これはC#にはない機能!文字列として直接使える。
実際に使ってみて感じた違い
Web APIでの使用例
C#の場合:
// JsonStringEnumConverterの設定が必要
public enum ApiStatus
{
Success,
Error
}
// レスポンス例: {"status": "Success"}
Pythonの場合:
from fastapi import FastAPI
from enum import StrEnum
class ApiStatus(StrEnum):
SUCCESS = "success"
ERROR = "error"
@app.get("/api/status")
def get_status():
return {"status": ApiStatus.SUCCESS} # 自動で文字列化
PythonのStrEnumは設定不要で文字列として扱えるので、API開発がとても楽でした。
データベース保存での違い
C#(Entity Framework):
// 設定が必要
modelBuilder.Entity<Order>()
.Property(e => e.Status)
.HasConversion<string>();
Python(SQLAlchemy):
from sqlalchemy import Enum as SqlEnum
class Order(Base):
# 文字列として保存したい場合
status = Column(SqlEnum(OrderStatus), nullable=False)
# または.valueを使う
status_value = Column(String, nullable=False)
SqlEnum を使うとデータベースにチェック制約が生成されます。単に文字列を保存したい場合は Column(String) + .value を使う方がシンプルです。
ハマったポイント:比較の違い
from enum import Enum
class Color(Enum):
RED = 1
BLUE = 2
# C#的な発想でやりがちな間違い
color_from_db = 1
if color_from_db == Color.RED: # False!
print("赤です")
# 正しい比較方法
if Color(color_from_db) == Color.RED: # True
print("赤です")
# または
if color_from_db == Color.RED.value: # True
print("赤です")
これは最初かなりハマりました。C#だと当たり前にできる比較が、Pythonでは明示的な変換が必要です。
ただし、color_from_db に存在しない値を渡すと ValueError が発生するので注意してください。
どのEnumを選ぶべき?使い分けガイド
実際に使ってみた結果、こんな使い分けをしています:
用途 | 推奨 | 理由 |
---|---|---|
API レスポンス | StrEnum |
JSON で文字列として直接出力される |
データベース保存(文字列) | StrEnum |
可読性が高い |
データベース保存(数値) | IntEnum |
パフォーマンス重視、既存システム連携 |
ビット演算が必要 |
Flag / IntFlag
|
権限管理など |
型安全重視 | Enum |
意図しない比較を防げる |
パフォーマンスで気をつけたこと
ループ処理でEnum比較を多用する場合は注意が必要です:
# 避けたい例(毎回Enum変換)
for item in large_list:
if item.status == OrderStatus.PENDING:
process_item(item)
# 改善例(事前に値を取得)
pending_value = OrderStatus.PENDING.value
for item in large_list:
if item.status == pending_value:
process_item(item)
C# vs Python:機能比較表
機能 | C# | Python |
---|---|---|
基盤型 | int固定(他の整数型指定可) | 任意型(int, str, tuple...) |
自動採番 | なし | auto() |
ビット演算 | [Flags] |
Flag / IntFlag
|
文字列Enum | 標準では不可 |
StrEnum (3.11+) |
値との直接比較 | 可能 |
IntEnum のみ可能 |
JSON シリアライズ | 設定で文字列化 |
.value またはStrEnum 使用 |
パターンマッチ |
switch 式 |
match 文 (3.10+) |
まとめ
C#とPythonのEnum、同じ名前でも結構違いました:
- C#:強い型安全性、数値ベース、シンプル
- Python:柔軟性、文字列対応、豊富なバリエーション
特にPythonのStrEnum
はAPI開発で本当に便利。C#でも同じような機能が欲しくなります。
一方で、型安全性を重視するならPythonでも基本のEnum
を使って、明示的な変換を心がける方が良い場面もあります。
どちらの言語でも「数値で保存するか、文字列で保存するか」をチーム内で統一しておくことが大切ですね。
参考リンク