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

在Python中向类添加保存功能的一般方法,python,metaprogramming,pickle,metaclass,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_som

我最近突然想到,使用某种形式的元编程来替换持久化所需的(公认是有限的)样板代码,然后使用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中不存在持久实例,则创建一个新实例
  • 系统在路径处找到一个文件,并调用pickle.load()读取它
  • pickle.load()对MyClass进行另一次调用。_init__;根据从磁盘读取的内容构建对象
  • MyClass.uuu init_uuu区分1和3的能力在我的实现中基本上是一个缺陷。有没有一种干净的方法可以做到这一点(无论是使用元类、装饰器还是其他什么)

    编辑:@roippi问,“听起来你好像在尝试重新实现搁置,不是吗?”是的,这个练习一开始是为了重新实现搁置,但允许任意键,这基本上就是我的syncpickledic。然后我发现我自己想要概括它。我已经修复了使用非字符串键的示例,使其更加清晰

    #!/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()))
    

    这可能适合@aIKid,我不知道codereview.stackexchange.com。我不知道哪个网站更合适,但这似乎对我来说是一个没有意义的问题,因为到目前为止,我还没有找到一种非版主在网站之间移动内容的方法。我会问“为什么使用元类?”这里。似乎常规子类化可以完成这项工作。我正在使用元类来拦截对实际类的uuu init_uu_u方法的调用,并说,“如果文件路径上有一个文件,只需加载它,而不是执行常规的uuu init_u_u_u_u_u。”我不想要求在使用此功能的每个类中都必须重新生成该逻辑。因为这样做的结果不太好,所以把这个问题贴在这里。我想我可以说,使用sync_pickle特性的类不能有一个_init__方法,而是应该提供一个方法,如果SyncPickle在filepath中找不到文件,它将调用该方法。这是最好的方法吗?可以创建实例而不调用它们的
    \uuuuu init\uuuuuu()
    :尝试
    SomeClass.\uuuuu new\uuuuuuuu(SomeClass)