np.ndarray
utilizza __reduce__
per sottaceti. Siamo in grado di dare uno sguardo a ciò che restituisce in realtà quando si chiama quella funzione per avere un'idea di quello che sta succedendo:
>>> obj = RealisticInfoArray([1, 2, 3], info='foo')
>>> obj.__reduce__()
(<built-in function _reconstruct>, (<class 'pick.RealisticInfoArray'>, (0,), 'b'), (1, (3,), dtype('int64'), False, '\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'))
Così, si ottiene un 3-tuple indietro. La documentazione per __reduce__
descrivono ciò che ogni elemento fa:
Quando una tupla viene restituita, deve essere compreso tra due e cinque elementi lunghi. Gli elementi facoltativi possono essere omessi oppure Nessuno può essere fornito come valore . Il contenuto di questa tupla viene decapato come normale e utilizzato per ricostruire l'oggetto al tempo di non prelievo. La semantica di ogni elemento sono:
Un oggetto richiamabile che verrà chiamato per creare la versione iniziale dell'oggetto. L'elemento successivo della tupla fornirà gli argomenti per questo callable e gli elementi successivi forniscono informazioni di stato aggiuntive che verranno successivamente utilizzate per ricostruire completamente i dati sottoposti a picking.
Nell'ambiente deserializzazione questo oggetto deve essere o una classe, un richiamabile registrato come “costruttore sicuro” (vedi sotto), o deve avere un attributo __safe_for_unpickling__
con un valore vero. In caso contrario, un UnpicklingError
verrà generato nell'ambiente deselezionante. Si noti che, come al solito, lo stesso callable viene decapitato dal nome .
Una tupla di argomenti per l'oggetto callable.
Facoltativamente, lo stato dell'oggetto, che verrà passato al metodo dell'oggetto __setstate__()
come descritto nella sezione decapaggio e normali istanze di classe deserializzare.Se l'oggetto non ha il metodo __setstate__()
, , come sopra, il valore deve essere un dizionario e verrà aggiunto a dell'oggetto __dict__
.
Quindi, _reconstruct
è la funzione chiamata per ricostruire l'oggetto, (<class 'pick.RealisticInfoArray'>, (0,), 'b')
sono gli argomenti passati a quella funzione, e (1, (3,), dtype('int64'), False, '\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'))
viene passato alla classe __setstate__
. Questo ci dà un'opportunità; potremmo ignorare lo __reduce__
e fornire la nostra tupla a __setstate__
, quindi in aggiunta ignorare __setstate__
, per impostare il nostro attributo personalizzato quando annulliamo il prelievo. Abbiamo solo bisogno di essere sicuri di conservare tutti i dati della classe genitore ha bisogno, e la chiamata del genitore __setstate__
, troppo:
class RealisticInfoArray(np.ndarray):
def __new__(cls, input_array, info=None):
obj = np.asarray(input_array).view(cls)
obj.info = info
return obj
def __array_finalize__(self, obj):
if obj is None: return
self.info = getattr(obj, 'info', None)
def __reduce__(self):
# Get the parent's __reduce__ tuple
pickled_state = super(RealisticInfoArray, self).__reduce__()
# Create our own tuple to pass to __setstate__
new_state = pickled_state[2] + (self.info,)
# Return a tuple that replaces the parent's __setstate__ tuple with our own
return (pickled_state[0], pickled_state[1], new_state)
def __setstate__(self, state):
self.info = state[-1] # Set the info attribute
# Call the parent's __setstate__ with the other tuple elements.
super(RealisticInfoArray, self).__setstate__(state[0:-1])
Usage:
>>> obj = pick.RealisticInfoArray([1, 2, 3], info='foo')
>>> pickle_str = pickle.dumps(obj)
>>> pickle_str
"cnumpy.core.multiarray\n_reconstruct\np0\n(cpick\nRealisticInfoArray\np1\n(I0\ntp2\nS'b'\np3\ntp4\nRp5\n(I1\n(I3\ntp6\ncnumpy\ndtype\np7\n(S'i8'\np8\nI0\nI1\ntp9\nRp10\n(I3\nS'<'\np11\nNNNI-1\nI-1\nI0\ntp12\nbI00\nS'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\np13\nS'foo'\np14\ntp15\nb."
>>> new_obj = pickle.loads(pickle_str)
>>> new_obj.info
'foo'
Grande, che ha riparato. Grazie anche per l'esempio di codice molto chiaro. In realtà sto trasferendo attraverso l'oggetto '__dict__' per renderlo più generico. Fortunatamente np.ndarray non * sembra * usarlo, quindi sono libero di usarlo per i miei scopi. – Gabriel