Domanda

Ho una macchina a stati definita dall'utente in ritorto. L'utente può definire i gestori per vari cambiamenti di stato, che implemento utilizzando un torto differito che lascio aggiungerli callback. Ogni volta che mi sposto da uno stato all'altro, ho semplicemente fuoco l'appropriata differito.

Uno dei requisiti del progetto è la possibilità di salvare questa macchina statale su disco, insieme a tutte le sue funzioni di callback. Ho pensato che avrei potuto semplicemente salamoia la macchina dello Stato e mi sarei fatto, ma ottengo un PickleError quando cerco di serializzare funzioni definite dall'utente.

Qualcuno sa di un modo per serializzare funzioni? L'errore viene riprodotto nell'esempio di codice qui sotto:

import pickle
from twisted.internet.utils import defer

def foo(*args):
  def bar():
    print args
  return bar

d = defer.Deferred()
d.addCallback(foo("Hello", "world"))
pickle.dumps(d)

Questa ultima linea dà il seguente errore:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/pickle.py", line 1366, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.5/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 725, in save_inst
    save(stuff)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "/usr/lib/python2.5/pickle.py", line 663, in _batch_setitems
    save(v)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 600, in save_list
    self._batch_appends(iter(obj))
  File "/usr/lib/python2.5/pickle.py", line 615, in _batch_appends
    save(x)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 562, in save_tuple
    save(element)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 562, in save_tuple
    save(element)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 748, in save_global
    (obj, module, name))
pickle.PicklingError: Can't pickle <function bar at 0xb753fe2c>: it's not found as __main__.bar

Ci sono delle soluzioni a questo? Forse ho bisogno di limitare i tipi di funzioni che gli utenti possono aggiungere come callback?

Grazie,
Jonathan

È stato utile?

Soluzione

Non cercare di fare la serializzazione Deferreds. Non è supportato da ritorto. Anche se si riesce a costruire qualcosa che sembra funzionare (e non è tutto impossibile), una versione successiva di Twisted potrebbe rompersi tutto il vostro stato salvato.

Deferreds sono per il controllo del flusso degli eventi attraverso il vostro codice. Non sono per la memorizzazione dello stato dell'applicazione. Se si vuole persistere vostro stato di applicazione, separarlo da qualsiasi Deferreds e serialize solo di esso.

Quando si esegue questa operazione, probabilmente vogliono anche evitare l'uso di salamoia per il formato di serializzazione. Pickle non è un buon modo per memorizzare i dati. Si tratta di un formato complesso elevato, che è molto sensibile ai cambiamenti di versioni di Python e le versioni della libreria. Essa non ha alcun mezzo per definire uno schema, in modo da non si può mai essere veramente sicuri che cosa state serializzare o quello che hai serializzato. E 'molto difficile per ispezionare una salamoia separatamente dal caricamento di esso, quindi se mai si rompe (come sarà se si decide di rinominare una classe che avete in salamoia istanze di), recuperare i dati è un importante problema.

Altri suggerimenti

Sostituire le funzioni foo / bar con un'istanza di classe callable:

class foo(object):
    def __init__(self, *args):
        self.args = args
    def __call__(self):
        print self.args

d = defer.Deferred()
d.addCallback(foo("Hello", "world"))
pickle.dumps(d)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top