在Python中向类添加保存功能的一般方法

时间:2013-11-03 17:20:20

标签: python metaprogramming pickle metaclass

我最近想到了使用某种形式的元编程来替换持久化然后使用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__方法变得复杂,因为发生了这种情况:

  1. 用户调用MyClass(path,other_args)打开现有的持久性实例,如果 path 不存在,则创建一个新的
  2. 系统在路径找到一个文件并调用pickle.load()来读取它
  3. pickle.load()再次调用MyClass .__ init__来根据从磁盘读取的内容构建对象
  4. MyClass .__ init __能够从#3告诉#1基本上是我实施中的黑客攻击。有没有一种干净的方法(无论是使用元类,装饰器还是其他)?

    编辑:@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()))
    

0 个答案:

没有答案