Как реализовать постоянный список «Python»?

Я пытаюсь сделать объект активным, как встроенный list , за исключением того, что его значение сохраняется после изменения.

Реализация, которую я придумал, – это перенос list в класс PersistentList . Для каждого доступа к методу, который может изменить список, оболочка делегирует завернутый list и сохраняет его в базу данных ключа после его вызова.

Код:

 class PersistentList(object): def __init__(self, key): self.key = key self._list = db.get(key, []) def __getattr__(self, name): attr = getattr(self._list, name) if attr: if attr in ('append', 'extend', 'insert', 'pop', 'remove', 'reverse', 'sort'): attr = self._autosave(attr) return attr raise AttributeError def _autosave(self, func): @wraps(func) def _(*args, **kwargs): ret = func(*args, **kwargs) self._save() return ret return _ def _save(self): db.set(self.key, self._list) 

В этой реализации есть несколько проблем:

  1. Я должен украшать такие методы, как append каждом доступе, есть ли лучший способ украсить несколько методов какого-либо объекта?

  2. Операции, подобные l += [1,2,3] , не работают, потому что я не реализовал метод iadd .

Что я могу сделать, чтобы упростить это?

4 Solutions collect form web for “Как реализовать постоянный список «Python»?”

Мне нравится ответ @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() 

синтаксис. По общему признанию, это не приводит к _save метода _save после каждой операции списка, только когда вы выходите из with-block . Но я думаю, что это дает вам достаточно возможности для сохранения, когда вы хотите сохранить, тем более, что __exit__ гарантированно будет выполняться независимо от того, как вы покинете with-block , в том числе, если это произойдет из-за исключения.

Вы можете быть преимуществом, что _save не вызывается после каждой операции списка. Представьте, что вы добавили в список 10000 раз. db.set индивидуальных вызовов db.set (базы данных?) db.set быть довольно трудоемким. Мне было бы лучше, по крайней мере с точки зрения производительности, сделать все добавления и сохранить один раз.


 class PersistentList(list): def __init__(self, key, db): self.key = key self.extend(db.get(key, [])) def _save(self): # db.set(self.key, self) print('saving {x}'.format(x = self)) def __enter__(self): return self def __exit__(self,ext_type,exc_value,traceback): self._save() db = {} p = PersistentList('key', db) with p: p.append(1) p.append(2) with p: p.pop() p += [1,2,3] # saving [1, 2] # saving [1, 1, 2, 3] 

Я знаю, что это не красиво или умно, но я бы просто написал отдельные методы …

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

Вот ответ, который очень похож на @ unutbu, но более общий. Он дает вам функцию, которую вы можете вызвать для синхронизации вашего объекта с диском, и он работает с другими классами, подходящими для разборки, помимо list .

 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() 
  • Проверка подписки на сообщение о покупке приложения для Android в Python в Google App Engine
  • печать разделенных табуляцией значений списка
  • Проверка соответствия уникальности при проверке формы в App Engine
  • Сравните, если две переменные ссылаются на один и тот же объект в python
  • Получение вызываемого объекта из кадра
  • GAE - Включает внешние модули python без добавления их в репозиторий?
  • Что такое Truthy и Falsy в python? Чем он отличается от Истины и Ложь?
  • Python - Как извлечь последние элементы x из списка
  • Python - лучший язык программирования в мире.