FC2ブログ

スポンサーサイト

-------- --:--:-- --

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

【Python】インスタンスをシリアライズする

2011-09-02 01:25:05 Fri

前置き



シリアライズ、と聞いてもわたしはピンとこないのですが、歴戦の勇士はピンとくるそうです。

文字列ではないような、例えばListやDictionaryといったインスタンスをシリアライズしてファイルに出力し、
これを入力してインスタンスにデシリアライズするといったことができます。

どんなメリットがあるかというと、そのインスタンスを他のプログラムでも扱うことが可能になるということです。

しかし、、、Pythonではpickleを利用することになるのですが、これはPythonのみ、しかも相手先のアプリケーションで同じクラスオブジェクトが必要なそうです。

じゃああんまり意味ないんじゃない?って声もあったりするようです。

僕はGoogleAppEngineでMemcacheにすごく大きなListインスタンスをつっこんでて、
MemcacheはListやDictionaryインスタンスだとI/O時間が大きくなってしまうとうわさ(真偽はわかりません)で聞いたので大きなデータサイズのListインスタンをシリアライズしてMemcacheに格納するなんてことをやってみました。

レスポンスはむしろ悪化してしまいました。

それは単純にロジックが悪いだけだと思います。

シリアライズの例



それでは本題です。

Pythonでインスタンスをシリアライズしようとするとpickleモジュールを利用します。


# ファイルを介さないシリアライズ/デシリアライズ
str = pickle.loads(['one', 'two', 'three']) # シリアライズ
array = pickle.dumps(str) #デシリアライズ

# ファイルを介すシリアライズ/デシリアライズ
array = ['one', 'two', 'three']
outfile = open('./serialize.dat', 'a+')
pickle.dump(array, outfile) # シリアライズ
outfile.close()

infile = open('./serialize.dat')
array2 = pickle.load(infile) # デシリアライズ
for e in array2:
print e



loads,dumps のようにsがつくとfileオブジェクトを介さないシリアライズ/デシリアライズで、
load,dumpのようにsがつかないとfileオブジェクトを介すシリアライズ/デシリアライズとなります。

参考URL
http://akas.cocolog-nifty.com/blog/2010/05/post-e285.html
http://lightson.dip.jp/blog/seko/1600
http://d.hatena.ne.jp/gumilab/20100929/1285726373
http://d.hatena.ne.jp/skobayas/20091213/1260707351

GoogleAppEngineで大きなListインスタンスをシリアライズしてMemcacheに保存



これ、作ってみましたが、処理速度、むしろ遅くなりました。
そのまま大きなListインスタンスをつっこんでるほうがいいかもしれません。
処理速度を犠牲にしてもMemcacheに大きなデータサイズのListインスタンを格納したい場合はご利用下さい。
たぶんちょっと変えればList以外でもDictionaryでもなんでもシリアライズして格納できます。
GoogleAppEngineのMemcacheはKEY-VALUEのペアで1MBの制限があります。
3MBのListを格納したいときは困るので、シリアライズして分割して格納、あとでMemcacheから取り出して結合してデシリアライズする、といった感じです。


def add_memcache(key, value, expires):
'''Memcacheに値を追加します。追加に成功した場合はTrue,失敗した場合はFalseを返却します。'''
logging.debug('add memcache key is ' + key)
flg = True
if not memcache.add(key, value, expires):
flg = False
logging.debug("Memcache set failed.")
else:
logging.debug("Memcache set success.")
return flg

def get_memcache(key):
'''Memcacheから値を取得します。値が取得できない場合はNoneを返却します。'''
logging.debug('get memcache key is ' + key)
ret = memcache.get(key)
if ret is not None:
logging.debug('Memcache get success.')
return ret
else:
logging.debug('Memcache get failed.')
return ret

def del_memcache(key):
'''
Memcacheから値を削除します。
削除に成功した場合有True,失敗した場合はFalseを返却します。
'''
logging.debug('del memcache key is ' + key)
ret = memcache.delete(key)
if ret == 0:
logging.debug('Memcache del success.')
return False
else:
logging.debug('Memcache del failed.')
return True

def flush_all_memcache():
'''
memcache 内のすべてを削除します。
戻り値は、成功した場合は True、RPC エラーまたはサーバー エラーの場合は False になります。
'''
return memcache.flush_all()

def get_stats_memcache():
'''
このアプリケーションの memcache 統計情報を取得します。さまざまな一時的条件により、この統計情報がすべてリセットされる場合があります。この統計情報は、呼び出された時点で利用できる最善の情報を提供します。
戻り値は、統計名とその値をマッピングするディクショナリです。統計情報と関連付けられた意味:
hits: 結果がキャッシュ ヒットとなるキャッシュ取得リクエスト数。
misses: 結果がキャッシュ ミスとなるキャッシュ取得リクエスト数。
byte_hits: 取得リクエスト時に変換された総バイト数。オーバーフローのときはゼロにロールオーバーします。
items: キャッシュ内のキー/値ペアの数。
bytes: キャッシュ内のすべてのアイテムのサイズ合計。
oldest_item_age: キャッシュ内の最も古いアイテムにアクセスがあったときからの秒数。新しいアイテムがアクセスされずにキャッシュに残る時間を示します。これはアイテムが作成されてから経過した時間数ではありません。
'''
return memcache.get_stats()

def conv_decoding(data):
"""
stringをunicodeへ変換する
@param ``data'' str object.
@return unicode object.
@return encoding.
"""
lookup = ('utf_8', 'euc_jp', 'euc_jis_2004', 'euc_jisx0213',
'shift_jis', 'shift_jis_2004','shift_jisx0213',
'iso2022jp', 'iso2022_jp_1', 'iso2022_jp_2', 'iso2022_jp_3',
'iso2022_jp_ext','latin_1', 'ascii')
encoding = ''
for encoding in lookup:
try:
data = data.decode(encoding)
break
except:
pass
return data, encoding

def conv_encoding(data, to_enc="utf_8"):
"""
stringのエンコーディングを変換する
@param ``data'' str object.
@param ``to_enc'' specified convert encoding.
@return str object.
"""
lookup = ('utf_8', 'euc_jp', 'euc_jis_2004', 'euc_jisx0213',
'shift_jis', 'shift_jis_2004','shift_jisx0213',
'iso2022jp', 'iso2022_jp_1', 'iso2022_jp_2', 'iso2022_jp_3',
'iso2022_jp_ext','latin_1', 'ascii')
for encoding in lookup:
try:
data = data.decode(encoding)
break
except:
pass
if isinstance(data, unicode):
return data.encode(to_enc)
else:
return data

def get_memcache_large_list(memcache_key):
'''
add_memcache_large_listメソッドを使用して分割された
サイズの大きなリストインスタンスを取得し返却します。
リストを取得できない場合、Noneを返却します。
'''
results = ''
division_number = get_memcache(memcache_key)
encoding = get_memcache(memcache_key + 'encoding')
logging.debug('分割数')
logging.debug(division_number)
logging.debug('エンコーディング')
logging.debug(encoding)
if division_number is None or type(division_number) != int:
results = None
return results
for i in range(division_number):
key = memcache_key + str(i)
value = get_memcache(key)
value = value.encode(encoding)
results = results + value
results = pickle.loads(results) if results != '' else None
if type(results) != list:
results = None
return results
else:
return results

def add_memcache_large_list(memcache_key, value, DIVISION_SIZE=800000):
'''
memcacheに保存するサイズの大きなリストを文字列にし、
分割して保存します。
'''
if type(value) != list:
logging.debug('Listではない型を指定しています。')
return False
temp = pickle.dumps(value)
uni, encoding = conv_decoding(temp)
bytesize = len(uni)
logging.debug('サイズ')
logging.debug(bytesize)
logging.debug('エンコーディング')
logging.debug(encoding)
add_memcache(memcache_key + 'encoding', encoding, MEMCACHE_EXPIRES) # encodingを保存
division_number = 1
if bytesize >= DIVISION_SIZE:
t1 = float(bytesize) / DIVISION_SIZE
division_number = int(math.ceil(t1))
add_memcache(memcache_key, division_number, MEMCACHE_EXPIRES) # 件数保存
for i in range(division_number):
if ((i + 1) * DIVISION_SIZE) <= bytesize:
start = i * DIVISION_SIZE
end = (i + 1) * DIVISION_SIZE
division_str = uni[start:end]
key = memcache_key + str(i)
add_memcache(key, division_str, MEMCACHE_EXPIRES)
else:
start = i * DIVISION_SIZE
end = bytesize
division_str = uni[start:end]
key = memcache_key + str(i)
add_memcache(key, division_str, MEMCACHE_EXPIRES)
else:
    add_memcache(memcache_key, division_number, MEMCACHE_EXPIRES) # 件数保存
for i in range(division_number):
key = memcache_key + str(i)
add_memcache(key, uni, MEMCACHE_EXPIRES)
logging.debug('分割数')
logging.debug(division_number)
return True

def del_memcache_large_list(memcache_key):
'''
add_memcache_large_listメソッドを利用して
設定されたMemcacheの値を削除します。
削除に成功した場合はTrue,失敗した場合はFalseを返却します。
'''
division_number = get_memcache(memcache_key)
if division_number is None:
return False
flg = del_memcache(memcache_key)
if not flg:
return False
flg = del_memcache(memcache_key + 'encoding')
if not flg:
return False
for i in range(division_number):
flg = del_memcache(memcache_key + str(i))
if not flg:
return False
return True



簡単に説明しておきます。

役者は次。


  • add_memcache_large_list(memcache_key, value, DIVISION_SIZE=800000)

  • get_memcache_large_list(memcache_key)

  • del_memcache_large_list(memcache_key)



add_memcache_large_listで保存したいMemcacheのキー名と大きなListインスタンスを渡します。
DIVISION_SIZEは分割するサイズで、1MBがGoogleAppEngineのMemcacheの制限になりますのでそれよりも小さい値を設定下さい。

このメソッドでは、「memcache_key」に分割数、「memcache_key + 'encoding'」にデシリアライズの際に使用するエンコーディング、「memcache_key + 0~…」にシリアライズされ分割された文字列をMemcacheに格納します。

get_memcache_large_listは、add_memcache_large_listで設定されたものをデシリアライズして取得します。

del_memcache_large_listは、add_memcache_large_listで設定されたMemcacheを削除します。

よかったらお使い下さいませ。
スポンサーサイト

⇒comment

Secret

名言集
全記事(数)表示
全タイトルを表示
ブログ内検索
Loading
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。