Desidero implementare il supporto del decapaggio per gli oggetti appartenenti alla mia libreria di estensioni. Esiste un'istanza globale di servizio di classe inizializzata all'avvio. Tutti questi oggetti sono prodotti come risultato di alcune invocazioni del metodo di servizio e in sostanza ne fanno parte. Il servizio sa come serializzarli in buffer binari e come deserializzare i buffer in oggetti.__reduce __/copy_reg python's unpickler semantico e statefull
Sembra che Pythons __ reduce__ debba servire al mio scopo: implementare il supporto per il decapaggio. Ho iniziato a implementarne uno e mi sono reso conto che c'è un problema con unpickler (primo elemento di una tupla che dovrebbe essere restituita da __ reduce__). Questa funzione di unpickle richiede l'istanza di un servizio per poter convertire il buffer di input in un oggetto. Ecco un po 'di pseudo codice per illustrare il problema:
class Service(object):
...
def pickleObject(self,obj):
# do serialization here and return buffer
...
def unpickleObject(self,buffer):
# do deserialization here and return new Object
...
class Object(object):
...
def __reduce__(self):
return self.service().unpickleObject, (self.service().pickleObject(self),)
Nota il primo elemento in una tupla. Il pickler Python non gli piace: dice che è instancemethod e non può essere decapitato. Ovviamente il pickler sta cercando di memorizzare la routine nell'output e vuole l'istanza del servizio insieme al nome della funzione, ma questo non è ciò che voglio che accada. Non voglio (e davvero non posso: il servizio non è selezionabile) per archiviare il servizio insieme a tutti gli oggetti. Voglio che l'istanza di servizio venga creata prima che pickle.load venga invocato e in qualche modo quell'istanza venga utilizzata durante l'annullamento.
Qui dove sono venuto dal modulo copy_reg. Di nuovo è apparso come dovrebbe risolvere i miei problemi. Questo modulo consente di registrare dinamicamente le routine di pickler e unpickler per tipo e queste dovrebbero essere usate in seguito per gli oggetti di questo tipo. Così ho aggiunto questa registrazione alla costruzione di servizi:
class Service(object):
...
def __init__(self):
...
import copy_reg
copy_reg(mymodule.Object, self.pickleObject, self.unpickleObject)
self.unpickleObject è ora un balzo servizio di metodo prendendo come primo parametro e buffer come secondo. self.pickleObject è anche un metodo vincolato che prende servizio e oggetto in pickle. copy_reg richiede che la routine pickleObject debba seguire la semantica del riduttore e restituisce tupla simile come in precedenza. E qui il problema è sorto di nuovo: cosa dovrei restituire come il primo elemento tuple ??
class Service(object):
...
def pickleObject(self,obj):
...
return self.unpickleObject, (self.serialize(obj),)
In questa forma sottaceto si lamenta ancora di non poter decapitare l'instancemethod. Ho provato None - non mi piace neanche. Ho messo lì una funzione fittizia. Funziona - il che significa che la fase di serializzazione è andata bene, ma durante l'annullamento chiama questa funzione fittizia invece di unpuncolare che ho registrato per il tipo mymodule.Object in Service constructor.
Quindi ora sono in perdita. Scusa per una lunga spiegazione: non sapevo come fare questa domanda in poche righe. Posso riassumere le mie domande come questa:
- Perché il copy_reg semantica mi obbliga a tornare di routine unpickler da pickleObject, se mi aspettavo di un registro unico modo indipendente?
- C'è qualche motivo per preferire l'interfaccia copy_reg.constructor per registrare la routine unpickler?
- Come faccio a rendere pickle per utilizzare il unpickler che ho registrato al posto di uno all'interno del flusso?
- Cosa devo restituire come primo elemento in una tupla come valore di risultato pickleObject? Esiste un valore "corretto"?
- Mi avvicino al tutto correttamente? C'è una soluzione diversa/più semplice?
Grazie per il vostro tempo.
Gennadiy
Non hai risposto Q1 sopra. –
Ho finito per fare qualcosa di simile. Anche se ho chiamato l'istanza di servizio globale, questo non è esattamente corretto. In effetti, esiste solo un'istanza utilizzata nell'applicazione di produzione, ma nulla dice che sia globale. E in effetti nei miei test unitari continuo a crearne uno nuovo ancora e ancora. Ho implementato un modulo pickle.py nel mio pacchetto con il registro di routine def (svc) e il servizio a livello di modulo globale variabile che viene impostato nel registro. Anche questa routine registra le funzioni di decapaggio/annullamento utilizzando copy_reg. Questa routine viene richiamata dal costruttore del servizio. Esistono tuttavia alcuni problemi: –
1. Funziona solo per una singola istanza di servizio. In altre parole, non possiamo avere due servizi diversi esistenti simultaneamente ed entrambi hanno oggetti selezionabili 2. Questo riferimento globale è fonte di potenziale perdita di memoria. Come e quando ho bisogno di ripulirlo? –