2012-05-31 18 views
10

Sto cercando consigli sui metodi di implementazione della persistenza dell'oggetto in Python. Per essere più precisi, desidero essere in grado di collegare un oggetto Python a un file in modo tale che qualsiasi processo Python che apre una rappresentazione di quel file condivida le stesse informazioni, qualsiasi processo possa cambiare il suo oggetto e le modifiche si propagheranno a gli altri processi e anche se tutti i processi "memorizzano" l'oggetto sono chiusi, il file rimarrà e potrà essere riaperto da un altro processo.Persistenza oggetto Python

Ho trovato tre candidati principali per questo nella mia distribuzione di Python - anydbm, pickle e shelve (dbm sembrava perfetto, ma è solo per Unix e io sono su Windows). Tuttavia, tutti hanno difetti:

  • anydbm può gestire solo un dizionario di valori stringa (sto cercando di memorizzare un elenco di dizionari, che hanno tutti le chiavi stringa e valori di stringa, anche se idealmente vorrei cercare un modulo senza restrizioni di tipo)
  • shelve richiede che un file venga riaperto prima della propagazione delle modifiche - ad esempio, se due processi A e B caricano lo stesso file (contenente una lista vuota accantonata) e A aggiunge un elemento al elenca e chiama sync(), B vedrà comunque l'elenco come vuoto finché non ricaricherà il file.
  • pickle (il modulo attualmente utilizzato per l'implementazione del test) ha lo stesso "requisito di ricarica" ​​come shelve e non sovrascrive i dati precedenti, se il processo A scarica quindici stringhe vuote su un file e quindi la stringa " ciao ', il processo B dovrà caricare il file sedici volte per ottenere la stringa' ciao '. Attualmente mi occupo di questo problema precedendo qualsiasi operazione di scrittura con letture ripetute fino alla fine del file ("pulendo la lavagna pulita prima di scriverle sopra") e facendo ogni operazione di lettura ripetuta fino alla fine del file, ma ritengo che ci debba essere un modo migliore.

mio modulo ideale sarebbe comportarsi come segue (con "A >>>" codice che rappresenta eseguito dal processo A, e "B >>>" codice eseguito dal processo B):

A>>> import imaginary_perfect_module as mod 
B>>> import imaginary_perfect_module as mod 
A>>> d = mod.load('a_file') 
B>>> d = mod.load('a_file') 
A>>> d 
{} 
B>>> d 
{} 
A>>> d[1] = 'this string is one' 
A>>> d['ones'] = 1 #anydbm would sulk here 
A>>> d['ones'] = 11 
A>>> d['a dict'] = {'this dictionary' : 'is arbitrary', 42 : 'the answer'} 
B>>> d['ones'] #shelve would raise a KeyError here, unless A had called d.sync() and B had reloaded d 
11 #pickle (with different syntax) would have returned 1 here, and then 11 on next call 
(etc. for B) 

I potrebbe ottenere questo comportamento creando il mio modulo che utilizza pickle e modificando il dump e il comportamento del caricamento in modo che utilizzino le letture ripetute che ho menzionato sopra, ma trovo difficile credere che questo problema non sia mai stato risolto e risolto da , programmatori più talentuosi prima. Inoltre, queste letture ripetute mi sembrano inefficienti (anche se devo ammettere che la mia conoscenza della complessità operativa è limitata, ed è possibile che queste letture ripetute stiano andando "dietro le quinte" in moduli altrimenti apparentemente più lisci come shelve). Pertanto, concludo che mi manca un modulo di codice che risolva il problema per me. Sarei grato se qualcuno potesse indicarmi la giusta direzione o dare consigli sull'implementazione.

+4

Dare un'occhiata a 'Mongo-db'. Non è completamente integrato nella lingua come nell'esempio sopra riportato, ma fornirà un database molto più robusto e tollerante rispetto al pickling sul filesystem e all'insegna dei lock. – slezica

risposta

11

Utilizzare invece il ZODB (il database degli oggetti Zope). Sostenuto con ZEO soddisfa le vostre esigenze:

  • persistenza trasparente per Python oggetti

    ZODB utilizza sottaceti sotto in modo tutto ciò che è in grado sottaceti possono essere memorizzati in un archivio di oggetti ZODB.

  • supporto delle transazioni compatibile ACID
  • completa (compresi i punti di salvataggio)

    Questo significa cambia da un processo di propagare a tutti gli altri processi quando sono buono e pronto, e ogni processo ha una visione coerente dei dati nel corso di una transazione.

ZODB è stato intorno per oltre un decennio, quindi tu sei proprio nel supponendo questo problema è già stato risolto prima. :-)

Lo ZODB consente di inserire i depositi; il formato più comune è FileStorage, che archivia tutto in un Data.fs con una memoria BLOB opzionale per oggetti di grandi dimensioni.

Alcuni storage ZODB sono wrapper attorno ad altri per aggiungere funzionalità; DemoStorage, ad esempio, mantiene le modifiche in memoria per facilitare le prove di unità e le impostazioni di dimostrazione (riavvia e hai ancora una volta una lavagna pulita). BeforeStorage fornisce una finestra temporale, restituendo solo i dati dalle transazioni prima di in un determinato momento. Quest'ultimo è stato determinante nel recuperare i dati persi per me.

ZEO è un tale plug-in che introduce un'architettura client-server. L'utilizzo di ZEO consente di accedere a una data memoria da più processi alla volta; non avrai bisogno di questo livello se tutto ciò di cui hai bisogno è l'accesso multi-thread da un solo processo.

Lo stesso potrebbe essere ottenuto con RelStorage, che memorizza i dati ZODB in un database relazionale come PostgreSQL, MySQL o Oracle.

+0

ZODB suona proprio come voglio (e RelStorage suona come qualcosa di interessante da verificare per il futuro) - grazie! Lo verificherò e tornerò per segnare come risposta se funziona per me. – scubbo

+0

Fantastico, grazie per il consiglio! – scubbo

+0

Questo suona come quello che voglio anch'io; su ciò che fornisce shelving. – fatuhoku

2

Per i principianti, è possibile porta i database Shelve ai database ZODB come questo:

#!/usr/bin/env python 
import shelve 
import ZODB, ZODB.FileStorage 
import transaction 
from optparse import OptionParser 
import os 
import sys 
import re 

reload(sys) 
sys.setdefaultencoding("utf-8") 

parser = OptionParser() 

parser.add_option("-o", "--output", dest = "out_file", default = False, help ="original shelve database filename") 
parser.add_option("-i", "--input", dest = "in_file", default = False, help ="new zodb database filename") 

parser.set_defaults() 
options, args = parser.parse_args() 

if options.in_file == False or options.out_file == False : 
    print "Need input and output database filenames" 
    exit(1) 

db = shelve.open(options.in_file, writeback=True) 
zstorage = ZODB.FileStorage.FileStorage(options.out_file) 
zdb = ZODB.DB(zstorage) 
zconnection = zdb.open() 
newdb = zconnection.root() 

for key, value in db.iteritems() : 
    print "Copying key: " + str(key) 
    newdb[key] = value 

transaction.commit()