dill

シリアライゼーションPythonpickle拡張バイナリ形式関数シリアライズ

シリアライゼーションライブラリ

dill

概要

dillは、Pythonの標準pickleモジュールを拡張したシリアライゼーションライブラリです。pickleでは扱えない複雑なPythonオブジェクト(ラムダ関数、ネストされた関数、クラス定義、ジェネレータなど)もシリアライズできる強力なツールです。インタープリタセッション全体の保存・復元も可能で、科学計算や機械学習の分野で特に重宝されています。

詳細

dillは、Pythonのpickleモジュールの機能を大幅に拡張したライブラリです。pickleと同じインターフェースを提供しながら、より多様なPythonオブジェクトのシリアライゼーションを可能にします。

主な特徴:

  • 幅広いオブジェクト対応: ラムダ関数、ネストされた関数、クロージャ、ジェネレータ、クラス定義など、pickleでは扱えないオブジェクトをシリアライズ
  • 定義の保存: オブジェクトの定義も一緒に保存するため、別環境での復元が容易
  • インタープリタセッション保存: Pythonインタープリタの状態を丸ごと保存・復元可能
  • pickle互換: pickleと同じAPIを提供し、ドロップイン置換として使用可能

技術的詳細:

  • byrefオプション: モジュールなどを参照で保存(pickle互換動作)
  • recurseオプション: グローバル辞書内のオブジェクトを再帰的に追跡
  • fmodeオプション: ファイルハンドルとファイル内容の保存方法を制御
  • protocolオプション: pickleプロトコルレベルの指定

メリット・デメリット

メリット

  • pickleの制限を超えた幅広いオブジェクトのシリアライゼーション
  • 関数やクラスの定義も含めて保存するため、復元時の依存性問題が少ない
  • インタープリタセッション全体の保存による作業の継続性
  • pickleと同じAPIのため、学習コストが低い
  • 科学計算や機械学習のワークフローで強力なツール

デメリット

  • セキュリティリスク(pickleと同様、信頼できないソースからのデータは危険)
  • ファイルサイズが大きくなりやすい(定義情報も含むため)
  • Python専用で他言語との相互運用性がない
  • バージョン間の互換性問題が発生する可能性

参考ページ

書き方の例

基本的な使用方法

import dill

# オブジェクトのシリアライズ
data = {'key': 'value', 'number': 42}
serialized = dill.dumps(data)

# デシリアライズ
restored = dill.loads(serialized)
print(restored)  # {'key': 'value', 'number': 42}

# ファイルへの保存
with open('data.pkl', 'wb') as f:
    dill.dump(data, f)

# ファイルからの読み込み
with open('data.pkl', 'rb') as f:
    loaded_data = dill.load(f)

ラムダ関数のシリアライズ

import dill

# ラムダ関数の定義
square = lambda x: x ** 2
add = lambda x, y: x + y

# シリアライズ
serialized_square = dill.dumps(square)
serialized_add = dill.dumps(add)

# デシリアライズして使用
restored_square = dill.loads(serialized_square)
restored_add = dill.loads(serialized_add)

print(restored_square(5))  # 25
print(restored_add(3, 4))  # 7

ネストされた関数とクロージャ

import dill

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

# クロージャを作成
closure = outer_function(10)

# シリアライズとデシリアライズ
serialized = dill.dumps(closure)
restored = dill.loads(serialized)

print(restored(5))  # 15

クラスとインスタンスのシリアライズ

import dill

class Calculator:
    def __init__(self, name):
        self.name = name
        self.history = []
    
    def add(self, a, b):
        result = a + b
        self.history.append(f"{a} + {b} = {result}")
        return result
    
    def get_history(self):
        return self.history

# インスタンスの作成と使用
calc = Calculator("My Calculator")
calc.add(5, 3)
calc.add(10, 20)

# シリアライズ
serialized = dill.dumps(calc)

# 別の場所でデシリアライズ(クラス定義も復元される)
restored_calc = dill.loads(serialized)
print(restored_calc.name)  # My Calculator
print(restored_calc.get_history())  # ['5 + 3 = 8', '10 + 20 = 30']

インタープリタセッションの保存

import dill

# 作業中のデータ
x = 42
y = "Hello, World!"
data = [1, 2, 3, 4, 5]

def process_data(lst):
    return [item * 2 for item in lst]

result = process_data(data)

# セッション全体を保存
dill.dump_session('session.pkl')

# 後で別のセッションで復元
# (新しいPythonセッションで実行)
import dill
dill.load_session('session.pkl')

# 保存した変数や関数が利用可能
print(x)  # 42
print(y)  # Hello, World!
print(result)  # [2, 4, 6, 8, 10]
print(process_data([10, 20, 30]))  # [20, 40, 60]

高度な設定オプション

import dill

# 複雑なオブジェクト
class DataProcessor:
    def __init__(self):
        self.transform = lambda x: x ** 2
        self.data = []
    
    def process(self, items):
        return [self.transform(item) for item in items]

processor = DataProcessor()

# 様々なオプションを使用してシリアライズ
# protocol: pickleプロトコルバージョン
# byref: True にすると参照で保存(pickle互換動作)
# recurse: True にすると再帰的に追跡
serialized = dill.dumps(
    processor,
    protocol=dill.HIGHEST_PROTOCOL,
    byref=False,
    recurse=True
)

# ファイルハンドルと内容の保存
with open('example.txt', 'w') as f:
    f.write("Hello, dill!")
    f.seek(0)
    
    # fmode オプション
    # - dill.HANDLE_FMODE: ハンドルのみ
    # - dill.CONTENTS_FMODE: ファイル内容
    # - dill.FILE_FMODE: 内容とハンドル
    file_data = dill.dumps(f, fmode=dill.FILE_FMODE)

# 復元
restored_file = dill.loads(file_data)