0

我最近想到了使用某种形式的元编程来替换(公认的限制)样板代码需要持久化,然后使用 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. 系统在path找到一个文件并调用 pickle.load() 来读取它
  3. 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()))
4

0 回答 0