Dynamic traits do not survive pickling

traits_pickle_problem.py

from traits.api import HasTraits, List
import cPickle

class Client(HasTraits):
   data = List

class Person(object):
   def __init__(self):
      self.client = Client()
      # dynamic handler
      self.client.on_trait_event(self.report,'data_items')

   def report(self,obj,name,old,new):
      print 'client added-- ' , new.added

if __name__ == '__main__':
   p = Person()
   p.client.data = [1,2,3]
   p.client.data.append(10)
   cPickle.dump(p,open('testTraits.pkl','wb'))

The above code reports a dynamic trait. Everything works as expected in this code. However, using a new python process and doing the following:

>>> from traits_pickle_problem import Person, Client                                              
>>> p=cPickle.load(open('testTraits.pkl','rb'))                                                   
>>> p.client.data.append(1000)  

causes no report of the list append. However, re-establishing the listener separately as follows:

>>> p.client.on_trait_event(p.report,'data_items')                                                
>>> p.client.data.append(1000)                                                                    
client added--  [1000]     

makes it work again.

Am I missing something or does the handler need to be re-established in __setstate__ during the unpickling process.

Any help appreciated. This is for Python 2.7 (32-bit) on windows with traits version 4.30.


Running pickletools.dis(cPickle.dumps(p)) , you can see the handler object being referenced:

  ...
  213: c        GLOBAL     'traits.trait_handlers TraitListObject'
  ...

But there's no further information on how it should be wired to the report method. So either the trait_handler doesn't pickle itself out properly, or it's an ephemeral thing like a file handle that can't be pickled in the first place.

In either case, your best option is to overload __setstate__ and re-wire the event handler when the object is re-created. It's not ideal, but at least everything is contained within the object.

class Person(object):
    def __init__(self):
        self.client = Client()
        # dynamic handler
        self.client.on_trait_event(self.report, 'data_items')

    def __setstate__(self, d):
        self.client = d['client']
        self.client.on_trait_event(self.report, 'data_items')

    def report(self, obj, name, old, new):
        print 'client added-- ', new.added

Unpickling the file now correctly registers the event handler:

p=cPickle.load(open('testTraits.pkl','rb'))
p.client.data.append(1000)
>>> client added--  [1000]

You might find this talk Alex Gaynor did at PyCon interesting. It goes into the high points of how pickling work under the hood.

EDIT - initial response used on_trait_change - a typo that appears to work. Changed it back to on_trait_event for clarity.


I had the same problem but came around like this: Imaging I want to pickle only parts of a quiet big class and some of the objects has been set so transient=True so they're not pickled because there is nothing important to save, eg

class LineSpectrum(HasTraits):
    andor_cam = Instance(ANDORiKonM, transient=True)

In difference to objects which should be saved, eg

spectrometer = Instance(SomeNiceSpectrometer)

In my LineSpectrum class, I have a

def __init__(self, f):
    super(LineSpectrum, self).__init__()
    self.load_spectrum(f)

def __setstate__(self, state):  # FUCKING WORKING!
    print("LineSpectrum: __setstate__ with super(...) call")
    self.__dict__.update(state)
    super(LineSpectrum, self).__init__()  # this has to be done, otherwise pickled sliders won't work, also first update __dict__!
    self.from_pickle = True  # is not needed by traits, need it for myself
    self.andor_cam = ANDORiKonM(self.filename)
    self.load_spectrum(self.filename)

In my case, this works perfectly - all sliders are working, all values set at the time the object has been pickled are set back.

Hope this works for you or anybody who's having the same problem. Got Anaconda Python 2.7.11, all packages updated.

PS: I know the thread is old, but didn't want to open a new one just for this.

链接地址: http://www.djcxy.com/p/19854.html

上一篇: Laravel:将参数传递给关系函数?

下一篇: 动态特征不能在酸洗中生存