Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/google-app-engine/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何实现持久化的Python`list`?_Python - Fatal编程技术网

如何实现持久化的Python`list`?

如何实现持久化的Python`list`?,python,Python,我试图使一个对象像一个内置的列表,只是它的值一旦修改就可以保存 我提出的实现是将列表包装到持久列表类中。对于每个可能更改列表的方法的访问,包装器都会委托给包装的列表,并在调用后将其保存到键值数据库 代码: 此实现存在几个问题: 每次我都要装饰像append这样的方法 访问后,是否有更好的方法来装饰某些对象的多种方法 反对 像l+=[1,2,3]这样的操作不起作用,因为我没有 实施iadd方法 我能做些什么来简化它呢?我知道它既不漂亮也不聪明,但我只想把各个方法写出来 class Persiste

我试图使一个对象像一个内置的
列表
,只是它的值一旦修改就可以保存

我提出的实现是将
列表
包装到
持久列表
类中。对于每个可能更改列表的方法的访问,包装器都会委托给包装的
列表
,并在调用后将其保存到键值数据库

代码:

此实现存在几个问题:

  • 每次我都要装饰像
    append
    这样的方法 访问后,是否有更好的方法来装饰某些对象的多种方法 反对

  • l+=[1,2,3]
    这样的操作不起作用,因为我没有 实施iadd方法


  • 我能做些什么来简化它呢?

    我知道它既不漂亮也不聪明,但我只想把各个方法写出来

    class PersistentList(object):
       ...
    
       def append(self, o):
          self._autosave()
          self._list.append(o)
    
       ...etc...
    

    我喜欢@andrew cooke的答案,但我认为没有理由不能直接从列表中得出答案

    class PersistentList(list):
        def __init__(self, *args, **kwargs):
            for attr in ('append', 'extend', 'insert', 'pop', 'remove', 'reverse', 'sort'):
                setattr(self, attr, self._autosave(getattr(self, attr))
            list.__init__(self, *args, **kwargs)
        def _autosave(self, func):
            @wraps(func)
            def _func(*args, **kwargs):
                ret = func(*args, **kwargs)
                self._save()
                return ret 
            return _func
    

    这里有一种避免修饰每个列表方法的方法。它使PersistentList成为一个,因此您可以使用

    with PersistentList('key', db) as persistent:
        do_stuff()
    
    语法。诚然,这不会导致在每次列表操作后调用
    \u save
    方法,仅当您退出带有block的
    时。但我认为它在您想要保存时为您提供了足够的控制来保存,特别是因为无论您如何将
    保留为block
    ,包括由于异常而发生的情况,
    \uuuuuuuuuuuuuuuuuuuu
    方法都保证执行

    您的一个优势可能是,在每次列表操作后都不会调用
    \u save
    。想象一下在列表中添加10000次。对
    db.set
    (数据库?)的这么多单独调用可能相当耗时。我会更好,至少从性能的角度来看,让所有的附件和保存一次



    这里有一个很像@unutbu的答案,但更一般。它为您提供了一个可以调用的函数,用于将对象同步到磁盘,并可与
    列表
    之外的其他可pickle类一起使用

    with pickle_wrap(os.path.expanduser("~/Desktop/simple_list"), list) as (lst, lst_sync):
        lst.append("spam")
        lst_sync()
        lst.append("ham")
        print(str(lst))
        # lst is synced one last time by __exit__
    
    下面是使这成为可能的代码:

    import contextlib, pickle, os, warnings
    
    def touch_new(filepath):
        "Will fail if file already exists, or if relevant directories don't already exist"
        # http://stackoverflow.com/a/1348073/2829764
        os.close(os.open(filepath, os.O_WRONLY | os.O_CREAT | os.O_EXCL))
    
    @contextlib.contextmanager
    def pickle_wrap(filepath, make_new, check_type=True):
        "Context manager that loads a file using pickle and then dumps it back out in __exit__"
        try:
            with open(filepath, "rb") as ifile:
                result = pickle.load(ifile)
            if check_type:
                new_instance = make_new()
                if new_instance.__class__ != result.__class__:
                    # We don't even allow one class to be a subclass of the other
                    raise TypeError(("Class {} of loaded file does not match class {} of "
                        + "value returned by make_new()")
                        .format(result.__class__, new_instance.__class__))
        except IOError:
            touch_new(filepath)
            result = make_new()
        try:
            hash(result)
        except TypeError:
            pass
        else:
            warnings.warn("You probably don't want to use pickle_wrap on a hashable (and therefore likely immutable) type")
    
        def sync():
            print("pickle_wrap syncing")
            with open(filepath, "wb") as ofile:
                pickle.dump(result, ofile)
    
        yield result, sync
        sync()
    

    如果您调用的某个列表方法引发异常怎么办?你还想保存吗?您当前的解决方案仍然可以做到这一点……如果您愿意,您可以通过混合这两种技术来获得更多的乐趣,这样您就可以保留一个“脏”标志,指示需要保存它。您甚至可以这样做,
    persistenlt.\uu del\uuu
    会抱怨或试图保存(如果系统退出,它可能会失败)。@ChrisMorgan:我喜欢您的想法,但我认为很难正确实施。例如,如果用户要
    追加
    ,那么
    弹出
    ,一个简单的实现(通过修饰每个列表方法)会错误地设置
    标志。为了做得更好,您需要在
    \uuuu\enter\uuu
    和每个列表方法测试中保存列表的副本(如果列表不干净)。所有这些比较可能会使性能变慢。因为一般来说,你会想存钱,也许最好是有点浪费,每次都存钱。我只把它作为一个基本指标,表明事情已经改变了。当然,更改可能已经撤消,但正如您所说,防止不必要的写入的成本太高了。@ChrisMorgan:啊,我明白了。在这种情况下,我认为我们可以假设,如果用户将
    与p
    一起使用,那么p将会改变。因此,我们可以想当然地认为脏旗会被设置。我们可以在
    中保存原始列表的副本,然后在
    中进行比较,看看是否需要调用
    \u save
    。这将避免装饰所有的列表方法。您的
    \u save
    是什么样子的?那么如何将对象加载回?我天真的尝试使用
    pickle
    这样做是行不通的<代码>pickle.dumps(self)
    不起作用,而
    pickle.dumps(list(self))
    起作用。还是每次运行
    \u save
    时都转换成一个列表()?还有,是什么让你确信你不必将
    '\u delitem'、'\u delslice'、'\u iadd'、'\u imul'、'\u反转的'\uu'、'\uu setitem'、'\uu setslice'
    包含在你的变体列表中?不知道save看起来像什么。我相信我是基于一个已经被删除的答案。您可能应该包括任何可以修改列表数据的函数,包括您提到的大多数函数(尽管我不认为reversed是一个变种)。我通过执行
    set(dir(list))-set(dir(tuple))
    得到了方法列表。我想创建迭代器需要以某种方式改变列表。OTOH
    反向
    (无下划线)用于元组。我是为了找出原因而创作的。
    with pickle_wrap(os.path.expanduser("~/Desktop/simple_list"), list) as (lst, lst_sync):
        lst.append("spam")
        lst_sync()
        lst.append("ham")
        print(str(lst))
        # lst is synced one last time by __exit__
    
    import contextlib, pickle, os, warnings
    
    def touch_new(filepath):
        "Will fail if file already exists, or if relevant directories don't already exist"
        # http://stackoverflow.com/a/1348073/2829764
        os.close(os.open(filepath, os.O_WRONLY | os.O_CREAT | os.O_EXCL))
    
    @contextlib.contextmanager
    def pickle_wrap(filepath, make_new, check_type=True):
        "Context manager that loads a file using pickle and then dumps it back out in __exit__"
        try:
            with open(filepath, "rb") as ifile:
                result = pickle.load(ifile)
            if check_type:
                new_instance = make_new()
                if new_instance.__class__ != result.__class__:
                    # We don't even allow one class to be a subclass of the other
                    raise TypeError(("Class {} of loaded file does not match class {} of "
                        + "value returned by make_new()")
                        .format(result.__class__, new_instance.__class__))
        except IOError:
            touch_new(filepath)
            result = make_new()
        try:
            hash(result)
        except TypeError:
            pass
        else:
            warnings.warn("You probably don't want to use pickle_wrap on a hashable (and therefore likely immutable) type")
    
        def sync():
            print("pickle_wrap syncing")
            with open(filepath, "wb") as ofile:
                pickle.dump(result, ofile)
    
        yield result, sync
        sync()