Mam kosztowną funkcję, która pobiera i zwraca niewielką ilość danych (kilka liczb całkowitych i liczb zmiennoprzecinkowych). Mam już tę funkcję, ale chciałbym, aby ta notatka była trwała. Istnieje już kilka wątków odnoszących się do tego, ale jestem pewien na temat potencjalnych problemów z niektórymi z proponowanych rozwiązań, a mam pewne dość specyficzne wymagania:Trwałe zapamiętywanie w Pythonie
- I na pewno użyć funkcji z wielu wątków i procesy jednocześnie (zarówno przy użyciu
multiprocessing
iz oddzielnych skryptów Pythona) - i nie trzeba czytać lub pisać dostęp do notatki spoza tej funkcji Pythona
- ja nie jestem tym zaniepokojony notatki są uszkodzone w rzadkich przypadkach (jak wyrywanie wtyczkę lub przypadkowo zapisując w pliku bez blokowania), ponieważ nie jest to drogi do odbudowy (zwykle 10-20 minut), ale wolałbym, żeby nie został uszkodzony z powodu wyjątków lub ręcznie przerwał proces python (nie wiem, jak realistyczny to jest)
- Zdecydowanie wolałbym rozwiązania które nie wymagają dużych bibliotek zewnętrznych, ponieważ mam bardzo ograniczoną ilość miejsca na dysku twardym na jednej maszynie będę uruchamiać kod na
- Mam słabe preferencje dla kodu między platformami, ale prawdopodobnie użyję tego tylko w systemie Linux:
This thread omawia moduł shelve
, który najwyraźniej nie jest bezpieczny dla procesu. Dwie z odpowiedzi sugerują użycie fcntl.flock
do zablokowania pliku półki. Niektóre z odpowiedzi w this thread wydają się sugerować, że jest to brzemienny problem - ale nie jestem do końca pewien, co to jest. Wydaje się, że jest to ograniczone do Uniksa (chociaż podobno Windows ma odpowiednik o nazwie msvcrt.locking
), a blokada jest tylko "doradcza" - to znaczy, nie powstrzyma mnie przed przypadkowym zapisaniem do pliku bez sprawdzenia, czy jest zablokowana. Czy są jakieś inne potencjalne problemy? Czy zapisanie kopii pliku i zastąpienie kopii wzorcowej końcowym krokiem zmniejszy ryzyko korupcji?
Nie wygląda na to, że dbm module będzie lepiej niż półka. Szybko rzuciłem okiem na sqlite3, ale wydaje mi się to nieco przesadzone. This thread i this one wspominają o bibliotekach stron trzecich, w tym o ZODB, ale istnieje wiele możliwości wyboru i wszystkie wydają się zbyt duże i skomplikowane w tym zadaniu.
Czy ktoś ma jakieś porady?
UPDATE: uprzejmie wspomniałem IncPy poniżej, co wygląda bardzo interesująco. Niestety nie chciałbym wrócić do Pythona 2.6 (w rzeczywistości używam wersji 3.2) i wygląda na to, że korzystanie z bibliotek C jest nieco niewygodne (między innymi używam potężnego numpy i scipy).
inny pomysł kindall jest pouczający, ale myślę, że dostosowanie go do wielu procesów byłoby trochę trudne - przypuszczam, że najłatwiej byłoby zastąpić kolejkę blokowaniem pliku lub bazą danych.
Patrząc ponownie na ZODB, wygląda idealnie na to zadanie, ale naprawdę chcę uniknąć używania dodatkowych bibliotek. Nadal nie jestem do końca pewien, jakie są wszystkie problemy związane z używaniem tylko flock
- wyobrażam sobie, że jednym dużym problemem jest to, czy proces jest kończony podczas zapisywania do pliku, czy przed zwolnieniem blokady?
Więc wziąłem porady synthesizerpatel i poszedł z sqlite3. Jeśli ktoś jest zainteresowany, postanowiłem zrobić zamiennik dla dict
który przechowuje swoje dane jak ogórki w bazie danych (nie przeszkadza, aby zachować w pamięci jako dowolny dostęp do bazy danych i marynowanie jest wystarczająco szybki w porównaniu do wszystkiego innego jestem robić). Jestem pewien, że są bardziej skuteczne sposoby osiągnięcia tego celu (i nie mam pojęcia, czy może nadal mam problemy współbieżności), ale tutaj jest kod:
from collections import MutableMapping
import sqlite3
import pickle
class PersistentDict(MutableMapping):
def __init__(self, dbpath, iterable=None, **kwargs):
self.dbpath = dbpath
with self.get_connection() as connection:
cursor = connection.cursor()
cursor.execute(
'create table if not exists memo '
'(key blob primary key not null, value blob not null)'
)
if iterable is not None:
self.update(iterable)
self.update(kwargs)
def encode(self, obj):
return pickle.dumps(obj)
def decode(self, blob):
return pickle.loads(blob)
def get_connection(self):
return sqlite3.connect(self.dbpath)
def __getitem__(self, key):
key = self.encode(key)
with self.get_connection() as connection:
cursor = connection.cursor()
cursor.execute(
'select value from memo where key=?',
(key,)
)
value = cursor.fetchone()
if value is None:
raise KeyError(key)
return self.decode(value[0])
def __setitem__(self, key, value):
key = self.encode(key)
value = self.encode(value)
with self.get_connection() as connection:
cursor = connection.cursor()
cursor.execute(
'insert or replace into memo values (?, ?)',
(key, value)
)
def __delitem__(self, key):
key = self.encode(key)
with self.get_connection() as connection:
cursor = connection.cursor()
cursor.execute(
'select count(*) from memo where key=?',
(key,)
)
if cursor.fetchone()[0] == 0:
raise KeyError(key)
cursor.execute(
'delete from memo where key=?',
(key,)
)
def __iter__(self):
with self.get_connection() as connection:
cursor = connection.cursor()
cursor.execute(
'select key from memo'
)
records = cursor.fetchall()
for r in records:
yield self.decode(r[0])
def __len__(self):
with self.get_connection() as connection:
cursor = connection.cursor()
cursor.execute(
'select count(*) from memo'
)
return cursor.fetchone()[0]
Jeśli można radzić sobie z Python 2.6.3 i nie są w systemie Windows, może chcesz sprawdzić [IncPy] (http://www.stanford.edu/~pgbovine/incpy.html), który będzie automatycznie uporczywie zapamiętuj swój * cały program * wszędzie, gdzie jest to bezpieczne. – kindall