私は最近、ピクルを使用して永続化し、オブジェクトをロードするために必要な (確かに制限されている) ボイラープレート コードを置き換えるために、何らかの形式のメタプログラミングを使用するというアイデアを思いつきました。だからあなたは次のようなものを得るでしょう
class MyClass(object):
# Something to add save-ability (maybe a decorator or metaclass)...
# More code...
my_instance = MyClass('/path/to/persistent_file')
my_instance.do_some_stuff()
my_instance.save()
my_instance._do_some_more_stuff()
my_instance.save()
メタクラスを使用してこれを達成しようとしました。残念ながら、私が望んでいたほどうまくいきませんでした。これが起こるため、__init__ メソッドは複雑になりました。
- ユーザーは MyClass(path, other_args) を呼び出して既存の永続インスタンスを開くか、pathに存在しない場合は新しいインスタンスを作成します
- システムはpathでファイルを見つけ、pickle.load() を呼び出してそれを読み取ります
- pickle.load() は MyClass.__init__ を再度呼び出して、ディスクから読み取った内容に基づいてオブジェクトを構築します
#1 と #3 を区別する MyClass.__init__ の機能は、基本的に私の実装のハックです。これを行うためのクリーンな方法はありますか (メタクラス、デコレータなどを使用するかどうか)?
編集: @roippi は、「シェルブを再実装しようとしているようですね?」と尋ねました。はい、この演習は、shelve を再実装する試みとして開始されましたが、基本的に私の SyncPickleDict である任意のキーを許可します。それから私はそれを一般化したいと思った。これをもう少し明確にするために、文字列以外のキーを使用するように例を修正しました。
#!/usr/bin/python2.7
import pickle
import os
import datetime
from collections import OrderedDict
class SyncPickleParent(object):
def __init__ (self, filepath, *args, **kwargs):
print "here and self is", self
self.__filepath = filepath
super(SyncPickleParent, self).__init__(*args, **kwargs)
def sync_pickle(self):
with open(self.__filepath, "w") as ofile:
pickle.dump(self, ofile)
class SyncPickle(type):
def __new__(meta, name, bases, dct):
return super(SyncPickle, meta).__new__(meta, name, (SyncPickleParent,)+bases, dct)
def __call__(cls, filepath, force_init=False, force_overwrite=False, *args, **kwds):
if force_init:
return type.__call__(cls, filepath, *args, **kwds)
if not force_overwrite:
try:
with open(filepath) as ifile:
red = pickle.load(ifile)
red._SyncPickleParent__filepath = filepath
return red
except IOError as ioe:
pass
result = type.__call__(cls, filepath, *args, **kwds)
result.sync_pickle() # Check that file is writable
return result
class SyncPickleDict(OrderedDict):
__metaclass__ = SyncPickle
def __init__(self, *args, **kwargs):
print "in init; args={}; kwargs={}".format(args, kwargs)
super(SyncPickleDict, self).__init__(*args, **kwargs)
def __reduce__(self):
# Yuck. The tuple output by __reduce__ has to have two bogus
# arguments
return (self.__class__, ("dummy", True, False, tuple(self.items())))
if "__main__" == __name__:
spd = SyncPickleDict(os.path.expanduser("~/tmp/bar.dat"))
spd[(1,2,3)] = 'foobar'
spd['access_count'] = spd.get('access_count', 0) + 1
spd[datetime.datetime.now()] = "A new key every time"
spd.sync_pickle()
print "\n".join(map(str, spd.iteritems()))