2012-05-31 19 views
10

Estoy buscando consejos sobre los métodos para implementar la persistencia de objetos en Python. Para ser más precisos, deseo poder vincular un objeto de Python a un archivo de tal manera que cualquier proceso de Python que abra una representación de ese archivo comparta la misma información, cualquier proceso puede cambiar su objeto y los cambios se propagarán a los otros procesos, e incluso si todos los procesos que "almacenan" el objeto están cerrados, el archivo permanecerá y puede ser reabierto por otro proceso.Persistencia de objeto Python

Encontré tres candidatos principales para esto en mi distribución de Python - anydbm, pickle, y shelve (dbm parecía ser perfecto, pero es de Unix solamente, y estoy en Windows). Sin embargo, todos ellos tienen defectos:

  • anydbm sólo puede manejar un diccionario de valores de cadena (estoy tratando de almacenar una lista de diccionarios, todos los cuales tienen claves de las cadenas y los valores de cadena, aunque idealmente me gustaría buscar una módulo sin restricciones de tipo)
  • shelve requiere que un archivo se vuelva a abrir antes de que se propaguen los cambios; por ejemplo, si dos procesos A y B cargan el mismo archivo (que contiene una lista vacía archivada) y A agrega un elemento al list y calls sync(), B seguirá viendo la lista como vacía hasta que vuelva a cargar el archivo.
  • pickle (el módulo que estoy usando actualmente para mi implementación de prueba) tiene el mismo "requisito de recarga" que shelve, y tampoco sobrescribe datos previos, si el proceso A vacía 180 cadenas vacías en un archivo, y luego la cadena ' hola ', el proceso B tendrá que cargar el archivo dieciséis veces para obtener la cadena' hola '. Actualmente estoy lidiando con este problema precediendo cualquier operación de escritura con lecturas repetidas hasta el final del archivo ("borrando la pizarra antes de escribir sobre ella"), y haciendo que cada operación de lectura se repita hasta el final del archivo, pero siento que debe haber una mejor manera.

Mi módulo ideal sería comportarse como sigue (con el código "A >>>" código que representa ejecutado por el proceso A, y "B >>>" ejecutado por el procedimiento 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 podría lograr este comportamiento creando mi propio módulo que usa pickle, y editando el volcado y el comportamiento de carga para que usen las lecturas repetidas que mencioné anteriormente, pero me cuesta creer que este problema nunca se haya producido y haya sido resuelto por , los programadores más talentosos antes. Además, estas lecturas repetidas me parecen ineficaces (aunque debo admitir que mi conocimiento de la complejidad de la operación es limitado, y es posible que estas repetidas lecturas se desarrollen "detrás de escena" en módulos aparentemente más suaves como shelve). Por lo tanto, concluyo que debo estar perdiendo algún módulo de código que me resolvería el problema. Estaría agradecido si alguien pudiera señalarme en la dirección correcta, o dar consejos sobre la implementación.

+4

Dar una mirada a 'mongo-db'. No está tan completamente integrado en el lenguaje como su ejemplo anterior, pero le dará una base de datos mucho más robusta y tolerante que el escalado al sistema de archivos y ser inteligente con los bloqueos. – slezica

Respuesta

11

Utilice en su lugar ZODB (la base de datos de objetos Zope). Apoyado con ZEO cumple sus requisitos:

  • persistencia transparente para Python objetos

    ZODB utiliza pepinillos debajo para que todo lo que es capaz de salmuera-puede ser almacenado en un almacén de objetos ZODB.

  • soporte de transacciones compatible con ACID
  • completo (incluidos los puntos de retorno)

    Esto significa cambios de un proceso se propagan a todos los otros procesos cuando son buenos y listos, y cada proceso tiene una visión consistente de los datos a través de una transacción.

ZODB existe desde hace más de una década, por lo que tiene razón al suponer que este problema ya ha sido resuelto antes. :-)

El ZODB le permite enchufar almacenes; el formato más común es el FileStorage, que almacena todo en un Data.fs con un almacenamiento blob opcional para objetos grandes.

Algunos almacenes ZODB son envoltorios alrededor de otros para agregar funcionalidad; DemoStorage, por ejemplo, mantiene los cambios en la memoria para facilitar las pruebas unitarias y las configuraciones de demostración (reinicie y tenga borrón y cuenta nueva). BeforeStorage le da una ventana en el tiempo, solo devuelve datos de las transacciones antes de en un punto determinado en el tiempo. Este último ha sido instrumental en la recuperación de datos perdidos para mí.

ZEO es un complemento que presenta una arquitectura cliente-servidor. El uso de ZEO le permite acceder a un almacenamiento dado desde múltiples procesos a la vez; no necesitarás esta capa si todo lo que necesitas es acceso de subprocesos múltiples desde un solo proceso.

Lo mismo se puede lograr con RelStorage, que almacena datos ZODB en una base de datos relacional como PostgreSQL, MySQL u Oracle.

+0

ZODB suena exactamente como yo quiero (y RelStorage suena como algo interesante para ver en el futuro) ¡gracias! Lo probaré y volveré a marcarlo como respondí si funciona para mí. – scubbo

+0

Impresionante, gracias por el consejo! – scubbo

+0

Esto suena como lo que quiero también; más de lo que proporciona shelve. – fatuhoku

2

Para los principiantes, usted puede transferir sus bases de datos a bases de datos de estantería ZODB como este:

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