はじめに
前回は namedtuple の基本を学びました。今回は、実務で役立つ活用パターンと、dataclasses との使い分けを解説します。
メソッドを追加する
namedtuple を継承してメソッドを追加できます。
from collections import namedtuple
class Point(namedtuple('Point', ['x', 'y'])):
"""2D座標を表すクラス"""
def distance_from_origin(self):
"""原点からの距離"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __add__(self, other):
"""座標の加算"""
return Point(self.x + other.x, self.y + other.y)
p1 = Point(3, 4)
print(p1.distance_from_origin()) # 5.0
p2 = Point(1, 2)
print(p1 + p2) # Point(x=4, y=6)
CSVデータの処理
CSVを namedtuple で読み込む
import csv
from collections import namedtuple
# CSVファイルを読み込む
csv_data = """name,age,city
田中,25,東京
山田,30,大阪
佐藤,28,福岡"""
from io import StringIO
with StringIO(csv_data) as f:
reader = csv.reader(f)
header = next(reader)
# ヘッダーから namedtuple を動的に作成
Row = namedtuple('Row', header)
rows = [Row(*row) for row in reader]
for row in rows:
print(f"{row.name}さん ({row.age}歳) - {row.city}")
# 田中さん (25歳) - 東京
# 山田さん (30歳) - 大阪
# 佐藤さん (28歳) - 福岡
データベースの行を表現
from collections import namedtuple
# テーブル定義に対応
User = namedtuple('User', ['id', 'name', 'email', 'created_at'])
# DBからの取得結果(通常はORMやカーソルから)
db_rows = [
(1, '田中', 'tanaka@example.com', '2024-01-01'),
(2, '山田', 'yamada@example.com', '2024-01-02'),
]
users = [User(*row) for row in db_rows]
for user in users:
print(f"ID:{user.id} - {user.name} ({user.email})")
設定値の管理
from collections import namedtuple
# アプリケーション設定
Config = namedtuple('Config', [
'db_host',
'db_port',
'debug',
'log_level'
], defaults=['localhost', 5432, False, 'INFO'])
# 開発環境
dev_config = Config(debug=True, log_level='DEBUG')
print(dev_config)
# Config(db_host='localhost', db_port=5432, debug=True, log_level='DEBUG')
# 本番環境
prod_config = Config(db_host='prod-db.example.com', db_port=5432)
print(prod_config)
# Config(db_host='prod-db.example.com', db_port=5432, debug=False, log_level='INFO')
APIレスポンスの型付け
from collections import namedtuple
import json
# APIレスポンスの構造を定義
ApiResponse = namedtuple('ApiResponse', ['status', 'data', 'message'])
UserData = namedtuple('UserData', ['id', 'name', 'email'])
def parse_api_response(json_str):
"""APIレスポンスをパース"""
raw = json.loads(json_str)
user_data = None
if raw.get('data'):
user_data = UserData(**raw['data'])
return ApiResponse(
status=raw['status'],
data=user_data,
message=raw.get('message', '')
)
# 使用例
json_response = '''
{
"status": "success",
"data": {"id": 1, "name": "田中", "email": "tanaka@example.com"},
"message": ""
}
'''
response = parse_api_response(json_response)
print(response.status) # success
print(response.data.name) # 田中
複数のnamedtupleを組み合わせる
from collections import namedtuple
# 住所
Address = namedtuple('Address', ['zip_code', 'city', 'street'])
# 会社
Company = namedtuple('Company', ['name', 'address', 'employees'])
# 従業員
Employee = namedtuple('Employee', ['name', 'role'])
# データ構築
company = Company(
name='テック株式会社',
address=Address('100-0001', '東京都千代田区', '丸の内1-1-1'),
employees=[
Employee('田中', 'エンジニア'),
Employee('山田', 'デザイナー'),
]
)
print(company.name) # テック株式会社
print(company.address.city) # 東京都千代田区
print(company.employees[0].name) # 田中
dataclasses との比較
Python 3.7以降は dataclasses も選択肢になります。
from collections import namedtuple
from dataclasses import dataclass
# namedtuple版
PointNT = namedtuple('PointNT', ['x', 'y'])
# dataclass版
@dataclass
class PointDC:
x: int
y: int
# 使用感はほぼ同じ
p1 = PointNT(10, 20)
p2 = PointDC(10, 20)
print(p1.x, p1.y) # 10 20
print(p2.x, p2.y) # 10 20
使い分け
| 特徴 | namedtuple | dataclass |
|---|---|---|
| 変更可能 | × | ◎(frozen=Trueで不可に) |
| 型ヒント | △ | ◎ |
| 継承 | △ | ◎ |
| メソッド追加 | △(継承が必要) | ◎ |
| メモリ効率 | ◎ | △ |
| Python 3.6以前 | ◎ | × |
namedtuple を選ぶ場面:
- イミュータブルなデータ
- メモリ効率が重要
- 辞書のキーとして使う
- Python 3.6以前のサポート
dataclass を選ぶ場面:
- 値を変更したい
- 型ヒントを活用したい
- メソッドを多く追加したい
- 継承を使いたい
typing.NamedTuple
型ヒント付きの namedtuple も使えます。
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int
label: str = "" # デフォルト値
p = Point(10, 20, "origin")
print(p) # Point(x=10, y=20, label='origin')
# 型ヒントが効く
def move(p: Point, dx: int, dy: int) -> Point:
return Point(p.x + dx, p.y + dy, p.label)
パフォーマンス比較(メモリ使用量)
import sys
from collections import namedtuple
from dataclasses import dataclass
# メモリ使用量の計測
PointNT = namedtuple('PointNT', ['x', 'y'])
point_nt = PointNT(10, 20)
@dataclass
class PointDC:
x: int
y: int
point_dc = PointDC(10, 20)
# オブジェクト本体のサイズ
print(f"namedtuple: {sys.getsizeof(point_nt)} bytes")
print(f"dataclass: {sys.getsizeof(point_dc)} bytes")
# dataclass の __dict__ サイズ(通常の dataclass のみ)
print(f"dataclass __dict__: {sys.getsizeof(point_dc.__dict__)} bytes")
注意:
sys.getsizeof()の測定について
sys.getsizeof()はオブジェクト本体のみのサイズを返します- 通常の dataclass は各インスタンスが
__dict__(属性辞書)を持つため、総メモリ使用量は上記の数値より大きくなります- 一方、namedtuple は tuple ベースのため
__dict__を持たず、上記の数値がほぼ総メモリ使用量となります- 実際の数値は Python バージョンや実行環境によって異なります
メモリ効率を重視する場合の選択肢
大量のオブジェクトを扱う場合など、メモリ効率を重視するなら以下が有効です:
| 選択肢 | 特徴 |
|---|---|
namedtuple |
tuple ベースで __dict__ なし。最もメモリ効率が良い |
typing.NamedTuple |
namedtuple と同等のメモリ効率 + 型ヒント対応 |
@dataclass(slots=True) |
Python 3.10以降。__slots__ により __dict__ を排除 |
# Python 3.10以降: slots=True でメモリ効率を改善
@dataclass(slots=True)
class PointSlots:
x: int
y: int
# slots=True の場合、__dict__ を持たない
point_slots = PointSlots(10, 20)
# hasattr(point_slots, '__dict__') # False
実践例:ログエントリの解析
from collections import namedtuple
from datetime import datetime
LogEntry = namedtuple('LogEntry', [
'timestamp',
'level',
'message',
'source'
])
def parse_log_line(line):
"""ログ行をパース"""
# 例: "2024-01-01 10:00:00 INFO Starting server [main.py]"
parts = line.split(' ', 3)
timestamp = datetime.strptime(f"{parts[0]} {parts[1]}", "%Y-%m-%d %H:%M:%S")
level = parts[2]
rest = parts[3]
message, source = rest.rsplit(' ', 1)
source = source.strip('[]')
return LogEntry(timestamp, level, message, source)
# 使用例
log_lines = [
"2024-01-01 10:00:00 INFO Starting server [main.py]",
"2024-01-01 10:00:01 ERROR Connection failed [db.py]",
]
entries = [parse_log_line(line) for line in log_lines]
# エラーログだけ抽出
errors = [e for e in entries if e.level == 'ERROR']
for e in errors:
print(f"[{e.source}] {e.message}")
まとめ
namedtuple は、CSVやデータベースの行、設定値、APIレスポンスなど、構造化されたデータを扱う場面で活躍します。継承によるメソッド追加や、複数の namedtuple を組み合わせた階層構造も表現できます。
Python 3.7以降では dataclasses も選択肢になりますが、イミュータブル性やメモリ効率を重視する場面では namedtuple が有利です。型ヒントを活用したい場合は typing.NamedTuple を検討しましょう。
namedtuple は軽量で効率的なデータ構造です。






