我最近想到了使用某种形式的元编程来替换(公认的限制)样板代码需要持久化,然后使用 pickle 加载对象。所以你会得到类似的东西
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__ 以根据从磁盘读取的内容构建对象
MyClass.__init__ 区分 #1 和 #3 的能力基本上是我实现中的一个技巧。有没有一种干净的方法来做到这一点(无论是使用元类、装饰器还是其他)?
编辑:@roippi 问道,“听起来你正在尝试重新实施搁置,不是吗?” 是的,这个练习开始是为了重新实现搁置,但允许任意键,这基本上就是我的 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()))