2012-09-28 14 views
6

Tengo que guardar y cargar una instancia de clase cython. Mi clase Cython es esto además de varios métodos:pickle cython clase

import numpy as np 
cimport numpy as np 
cimport cython  
cdef class Perceptron_avg_my: 
    cdef int wlen,freePos 
    cdef np.ndarray w,wtot,wac,wtotC#np.ndarray[np.int32_t] 
    cdef np.ndarray wmean #np.ndarray[np.float32_t]  
    cdef public dict fpos  

    def __cinit__(self,np.int64_t wlen=4*10**7): 
     self.fpos= dict() 
     self.freePos=1 
     self.wlen=wlen 
     self.w=np.zeros(wlen,np.int32) 
     self.wtot=np.zeros(wlen,np.int32) 
     self.wac=np.zeros(wlen,np.int32) 
     self.wtotc=np.zeros(wlen,np.int32) 
     self.wmean=np.zeros(wlen,np.float32) 

    cpdef evaluate_noavg(self,list f): 
     cdef np.ndarray[np.int32_t] w = self.w 
     cdef dict fpos = self.fpos   
     cdef bytes ff 
     cdef int i 
     cdef long int score=0 

     for ff in f: 
      i=fpos.get(ff,0) 
      if i != 0: 
       score += w[i] 
     return score 

estaba pensando usar el módulo cPickle. entiendo que tengo que poner en práctica un método de (auto) __ __reduce pero tengo algunos problemas para encontrar un ejemplo y para entender bien la documentación

He intentado añadir algo como esto para Perceptron_avg_my pero no las obras:

def rebuild(self,l): 
     self.fpos=l[0] 
     self.freePos=l[1] 

    def __reduce__(self): 
     #print 'reduce call' 
     return (Perceptron_avg_my.rebuild,(self.fpos,self.freePos)) 

alguna sugerencia? Muchas gracias!

+0

útil discusión aquí también: https://ask.sagemath.org/question/8376/pickling-extension-classes/ –

Respuesta

9

No sé si lo encontraste, pero la documentación oficial de Python tiene a section on pickling extension types (desafortunadamente no parece haber una versión de Python 3 de este documento, pero funciona igual en Python 3).

Creo que tiene tres problemas aquí. En primer lugar, se supone que la función devuelta por __reduce__ crea un nuevo objeto desde cero y lo devuelve, mientras que su función rebuild simplemente establece algunos atributos. En segundo lugar, la tupla devuelta por __reduce__ debe ser en sí misma seleccionable, y como método, Perceptron_avg_my.rebuild no es seleccionable (creo que se espera que esto se solucione en python 3.3 o 3.4). En cambio, podría convertirlo en una función de nivel de módulo. Finalmente, los argumentos (self.fpos,self.freePos) se pasan a rebuild individualmente - no tiene que descomprimir la tupla usted mismo.

El siguiente parece que funciona para mí (aunque es probable que desee almacenar los valores de los otros atributos también, de lo contrario, sólo tendrán los valores iniciales establecidos por __init__):

#inside the class definition 
def __reduce__(self): 
    return (rebuild, (self.wlen, self.fpos, self.freePos)) 

#standalone function 
def rebuild(wlen, fpos, freePos): 
    p = Perceptron_avg_my(wlen) 
    p.fpos = fpos 
    p.freePos = freePos 
    return p 
+1

gracias James y lo siento si respondo ahora mismo !!! Creo que también hay otro problema: la función devuelta por \ __ reduce__ no puede estar en el módulo cython (sinceramente no entiendo por qué). Agregaré una respuesta con una solución alternativa que me funcione ... pero no sé si hay una solución mejor. – Francesco

+0

Eso es extraño: me funciona cuando está definido dentro del módulo. No usaste accidentalmente 'cdef' en lugar de' def' para definir la función, ¿o sí? O tal vez solo estamos usando diferentes versiones de cython o python (lo intenté con python 3.2.3/cython 0.17, y python 2.7.2/cython 0.15.1). – James

+0

Uso python 2.7.3/cython 0.17. – Francesco

3

he utilizado esta solución eso funciona, pero no estoy seguro de que sea la mejor solución.

He creado un nuevo archivo de soporte para declarar la función llamada por reducir (si lo pongo en el módulo Cython no funciona):

#perceptron_supp.py 

from perceptron import Perceptron 

def rebuild_perceptron(wlen,freePos,fpos,w,nw_avg,wtot_avg,wsup_avg,wmean_avg,wtot_my,wac_my,wtotc_my,wmean_my): 
    return Perceptron(wlen,True,freePos,fpos,w,nw_avg,wtot_avg,wsup_avg,wmean_avg,wtot_my,wac_my,wtotc_my,wmean_my) 

y luego puedo importar esta función en el módulo Cython:

#perceptron.pyx 

import numpy as np 
cimport numpy as np 
cimport cython 

#added 
from perceptron_supp import rebuild_perceptron 

cdef class Perceptron: 
    cdef int wlen,freePos 
    cdef dict fpos 

    cdef np.ndarray w #np.ndarray[np.int32_t] 

    cdef int nw_avg 
    cdef np.ndarray wtot_avg,wsup_avg #np.ndarray[np.int32_t] 
    cdef np.ndarray wmean_avg #np.ndarray[np.float64_t] 

    cdef np.ndarray wtot_my,wac_my,wtotc_my #np.ndarray[np.int32_t] 
    cdef np.ndarray wmean_my #np.ndarray[np.float64_t] 

    def __cinit__(self,int wlen=4*10**7,setValues=False,freePos=0,fpos=0,w=0,nw_avg=0,wtot_avg=0,wsup_avg=0,wmean_avg=0,wtot_my=0,wac_my=0,wtotc_my=0,wmean_my=0): 
     if not setValues:    
      self.wlen=wlen 
      self.freePos=1 
      self.fpos= dict() 

      self.w=np.zeros(wlen,np.int32) 

      self.nw_avg=1 
      self.wtot_avg=np.zeros(wlen,np.int32)    
      self.wsup_avg=np.zeros(wlen,np.int32) 
      self.wmean_avg=np.zeros(wlen,np.float64) 

      self.wtot_my=np.zeros(wlen,np.int32)  
      self.wac_my=np.zeros(wlen,np.int32) 
      self.wtotc_my=np.zeros(wlen,np.int32) 
      self.wmean_my=np.zeros(wlen,np.float64) 
     else:   
      self.wlen=wlen 
      self.freePos=freePos 
      self.fpos=fpos 

      self.w=w 

      self.nw_avg=nw_avg 
      self.wtot_avg=wtot_avg 
      self.wsup_avg=wsup_avg 
      self.wmean_avg=wmean_avg 

      self.wtot_my=wtot_my 
      self.wac_my=wac_my 
      self.wtotc_my=wtotc_my 
      self.wmean_my=wmean_my 

    def __reduce__(self): 
     return (rebuild_perceptron,(self.wlen,self.freePos,self.fpos,self.w,self.nw_avg,self.wtot_avg,self.wsup_avg,self.wmean_avg,self.wtot_my,self.wac_my,self.wtotc_my,self.wmean_my)) 

cuando uso mi módulo de perceptron lo tengo que hacer: desde perceptron importo Perceptron y ahora puedo hacer cPyckle.dump o cPickle.load cuando lo necesite.

Si alguien tiene una solución mejor muchas gracias !!!

3

A partir de Cython 0.26 (lanzado en julio de 2017), la implementación del protocolo pickle ya no es necesaria. Todas las clases de cdef que no contienen punteros o uniones se pueden escalar automáticamente. Para las clases que contienen estructuras, el decapado automático está deshabilitado de manera predeterminada, debido a (entre otras razones) una sobrecarga de código alta. El decapado automático se puede habilitar para las clases con estructuras usando el decorador @cython.auto_pickle(True).

Más información se puede encontrar en changelog y en the website of Stefan Behnel.

+1

¡Me alegro de ver el nuevo lanzamiento! Sin embargo, todavía tenía que implementar mi propio mecanismo de decapado b/c 'TypeError: no predeterminado __reduce__ debido a no trivial __cinit__' –