6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Python] namedtuple 実践編 - 実務での活用パターン

6
Last updated at Posted at 2025-12-27

はじめに

前回は namedtuple の基本を学びました。今回は、実務で役立つ活用パターンと、dataclasses との使い分けを解説します。

namedtuple-vs-dataclass.png

メソッドを追加する

namedtuple-add-methods.png

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データの処理

image.png

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歳) - 福岡

データベースの行を表現

image.png

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})")

設定値の管理

image.png

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レスポンスの型付け

image.png

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を組み合わせる

image.png

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 は軽量で効率的なデータ構造です。

解説動画 (自分用)

6
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?