【问题标题】:Dynamic traits do not survive pickling动态特征不能在酸洗中存活
【发布时间】:2014-04-12 16:37:07
【问题描述】:

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'))

上面的代码报告了一个动态特征。这段代码中的一切都按预期工作。但是,使用 new python 进程并执行以下操作:

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

导致列表追加无报告。但是,单独重新建立监听器如下:

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

让它再次工作。

我是否遗漏了什么,或者是否需要在 unpickling 过程中在 __setstate__ 中重新建立处理程序。

任何帮助表示赞赏。这适用于具有 4.30 特性的 Windows 上的 Python 2.7(32 位)。

【问题讨论】:

  • 我查看了hastraits 源,找不到任何关于它如何存储回调处理程序的有用信息。我太不耐烦了。无论如何,问题似乎不在于您的代码,而在于特性。我觉得它很hacky,但我认为 setstate 是要走的路。请记住,setstate 接收实例 dict 作为参数,您必须在重新建立处理程序之前将其分配给 self.__dict__。

标签: python traits pickle


【解决方案1】:

运行pickletools.dis(cPickle.dumps(p)),可以看到被引用的handler对象:

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

但是没有关于它应该如何连接到report 方法的更多信息。所以要么 trait_handler 没有正确地腌制自己,要么它是一个短暂的东西,比如一开始就不能腌制的文件句柄。

在任何一种情况下,您最好的选择是重载__setstate__ 并在重新创建对象时重新连接事件处理程序。这并不理想,但至少所有内容都包含在对象中。

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

现在取消选择文件可以正确注册事件处理程序:

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

您可能会觉得this talk Alex Gaynor did at PyCon 很有趣。它深入探讨了酸洗如何在幕后工作。

编辑 - 使用了on_trait_change 的初始回复 - 一个似乎有效的错字。为了清楚起见,将其改回on_trait_event

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,但遇到了这样的问题:成像我只想腌制安静的大类的一部分,并且一些对象已设置为瞬态=真,因此它们不会腌制,因为没有什么重要的保存,例如

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

    与应保存的对象不同,例如

    spectrometer = Instance(SomeNiceSpectrometer)
    

    在我的 LineSpectrum 类中,我有一个

    def __init__(self, f):
        super(LineSpectrum, self).__init__()
        self.load_spectrum(f)
    
    def __setstate__(self, state):  # 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)
    

    在我的情况下,这非常有效 - 所有滑块都在工作,在对象被腌制时设置的所有值都被设置回来。

    希望这对您或遇到同样问题的任何人都有效。获得 Anaconda Python 2.7.11,所有包都更新了。

    PS:我知道线程很旧,但不想为此打开一个新线程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-15
      • 2012-07-24
      • 2014-08-13
      • 2021-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多