技術

関数をシリアライズ

Pythonにはpickleモジュールというシリアライズ用のモジュールが用意されています。
色んなものをシリアライズ・デシリアライズ可能というスグレモノなんですが、”Pythonに元々用意されている基本的なデータ型のみを利用したデータ”以外のものをシリアライズして、別のプログラムでデシリアライズする際には注意を要します。

>>> import pickle
>>> d = dict(a = 'aa', b = 'bb', c = range(3))
>>> pickle.dumps(d)
"(dp0\nS'a'\np1\nS'aa'\np2\nsS'c'\np3\n(lp4\nI0\naI1\naI2\nasS'b'\np5\nS'bb'\np6\ns."
>>> 
>>> def f():
...     print 'test'
... 
>>> pickle.dumps(f)
'c__main__\nf\np0\n.'
>>> 
>>> class c(object):
...     def test(self):
...             print 'test'
... 
>>> pickle.dumps(c)
'c__main__\nc\np0\n.'
>>> 

この対話モードでの結果を見ると分かると思いますが、関数やクラスの定義の内容はシリアライズされません。
つまり基本的に関数やクラスをシリアライズしたプログラムでしかデシリアライズ出来ませんし、別のプログラムで正常にデシリアライズするためにはそちらで全く同じ関数やクラスを定義しておく必要があります。
(シリアライズ・デシリアライズ可能な前提条件を満たすと、シリアライズ・デシリアライズする必要がなくなるという…)

基本的にpickleは別のプログラム間ではデータのやりとりにしか使えない、という事が言えると思います。

では、関数やクラスの定義の内容をシリアライズして別のプログラムでデシリアライズし実行するにはどうすればいいか。
大まかには、定義の文字列化(シリアライズ)、文字列をPythonインタープリタに解釈させる(デシリアライズ)、その結果を利用する(実行)という手順でいけそうです。

とりあえず関数の例をあげると以下のようになります。

シリアライズ元

# getsourcelinesは対話モードでは使えないので注意
from inspect import getsourcelines
def f():
    print 'test'

s = ''.join(getsourcelines(f)[0])

デシリアライズ先

# シリアライズ元の変数sを何らかの方法で受け取った後
code = compile(s, '', 'exec')
_locals = {}
exec(code, {}, _locals)
f = _locals.values()[0]
# f()で元の関数を実行可能

ただしこの方法は再帰的なシリアライズは行ってないので、独自に定義したグローバル変数(モジュール、関数、クラス)をシリアライズする関数内で利用してると、デシリアライズ先で実行時にエラーが出るはずです。

デシリアライズ先をサーバプログラム化して、TCPで待ち受けさせて、要求がきたら実行して結果を返す。という風にすると制限はありますがPythonサーバ(CPUサーバ?)みたいなものが比較的簡単に出来るんじゃないかと思います。
さらに共有ストレージを実装し、簡易的なMapReduceな仕組みにすれば、スケールアウトも簡単にできそうです。

というか実はMapReduceのプロトタイプの完成まであと少しで、幾つかの制限を緩和する方法が見つかれば…というところなんですが、すこしモチベーションが下がってきたので、記事を書いてみた次第であります。

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください