1. はじめに

1.1. この項目について

pythonでシリアライズする方法についてのメモ

1.2. pickleモジュールについて

pickleモジュールをつかって、デシリアライズする際に、任意のコードを実行させることができるそうで、セキュリティー云々言われています。

でもね。
結局のところ、信頼関係の話だと思うんですよ。
怪しいファイルを実行すれば、pickleモジュールを使っていようが、使っていまいが、怪しいことが起こりますよ、そりゃ。怪しいおっさんから「ぼぼぼ、ボクの作った、ハンバーガー、た、食べてほしぃんだな、ぐへへへ、」とか言って買わされたハンバーガーに何の疑いも持たずにかぶりつけば、泡吹いて倒れますよ。ピクルスが入っていようがいまいが。

要するに信頼できるコード(データ)のみを実行するという当然のことを守れば大丈夫かと。
だから、あんまり目くじらたてる必要はないと思います。

ただ、webから投稿されたデータをpythonで処理する際にpickleモジュールを使う、なんて際には気をつけたほうがいいのかもしれんけど。




2. 組み込み型の(デ)シリアライズ

2.1. 平文の場合

import pickle

#serialize
data = {22:[3,4,""], 55:"hello"}
se = pickle.dumps(data)
try:
	f = open("filename","wb")
except:
	print "error"
f.write(se)
f.close()

#desirialize
try:
	f = open("filename","rb")
except:
	print "error"
de = pickle.loads(f.read())
f.close()




2.2. 暗号化する場合

import pickle
import struct

#serialize
data = {22:[3,4,""], 55:"hello"}
se = pickle.dumps(data)
try:
	f = open("filename","wb")
except:
	print "error"

for i in se:
	#xor enc
	tmp = struct.unpack('B',i)[0] ^ 112
	f.write(struct.pack('B',tmp))
f.close()

#desirialize
try:
	f = open("filename","rb")
except:
	print "error"
de_tmp=""
for i in f.read():
	tmp= struct.unpack('B',i)[0] ^ 112
	de_tmp+=struct.pack('B', tmp)
de = pickle.loads(de_tmp)
f.close()




3. クラスの(デ)シリアライズ

3.1. クライス定義と同一ファイル内での(デ)シリアライズ

import pickle

class Myclass():
	def __init__(self):
		self.data={"aa": 445, "cc":2439, "gg":94320}
myc = Myclass()

#serialize
se = pickle.dumps([myc.__class__.__name__,myc.__dict__])
try:
	f = open("filename","wb")
except:
	print "error"
f.write(se)
f.close()

#desirialize
try:
	f = open("filename","rb")
except:
	print "error"
de_tmp = pickle.loads(f.read())
#get instance
de = globals()[de_tmp[0]]()
#set member
for k, v in de_tmp[1].items():
	de.__dict__[k] = v
f.close()




3.2. クラス定義と別ファイルでの(デ)シリアライズ

3.2.1. 問題点

・desirializeする時に、globals()["classname"]()が使えない
 →クラス定義したファイルと異なるので例外を出して失敗する

・getattr(__import__("libname"), "classname")()を使ってinstance生成
 →インポートパスが通ってないならsys.path.append(path_to_lib)も必要
 →しかしgetattrを使う側はどうやってlibname,classname,path_to_libを知るのか?
 →__file__[:-3]などとしてlibnameを取得できるのは、クラス定義をしたファイルのみ
  →getattr呼出側ではできない
 →予め登録するなどという面倒なことをするか?
 →登録すべき数が膨大になったらどうするか?


3.2.2. 指定クラスを派生させる方法

base.py(シリアライズする側)




test.py(クラス定義がある側)



クラス定義のところで、
class Myclass(Baseclass):
	def __init__(self):
		Baseclass.__init__(self,__file__)

としています。
ここで、呼び出されたBaseclass.__init__によって、Myclassは、自己のインポートパス、ライブラリ名、クラス名を保持することを強要されています。
これらの情報は、getattrを使う際に必要となるものです。 

ここまで書いて気づいたけど、Baseclass._import_pathもシリアライズすると良いかもね。