Python:检查对象是否可以原子化地进行pickle

Python:检查对象是否可以原子化地进行pickle,python,pickle,Python,Pickle,检查对象是否可以原子化酸洗的准确方法是什么?当我说“原子酸洗”,我的意思是不考虑它可能涉及的其他对象。例如,此列表: l = [threading.Lock()] 不是可pickle对象,因为它引用了不可pickle的锁。但从原子的角度来看,这个列表本身是可以修改的 那么,如何检查一个对象是否是原子可酸洗的呢?(我猜应该在课堂上做检查,但我不确定。) 我希望它的行为如下: >>> is_atomically_pickleable(3) True >>> is

检查对象是否可以原子化酸洗的准确方法是什么?当我说“原子酸洗”,我的意思是不考虑它可能涉及的其他对象。例如,此列表:

l = [threading.Lock()]
不是可pickle对象,因为它引用了不可pickle的
锁。但从原子的角度来看,这个列表本身是可以修改的

那么,如何检查一个对象是否是原子可酸洗的呢?(我猜应该在课堂上做检查,但我不确定。)

我希望它的行为如下:

>>> is_atomically_pickleable(3)
True
>>> is_atomically_pickleable(3.1)
True
>>> is_atomically_pickleable([1, 2, 3])
True
>>> is_atomically_pickleable(threading.Lock())
False
>>> is_atomically_pickleable(open('whatever', 'r'))
False

等等。

考虑到Python的动态特性,我认为除了启发式或白名单之外,没有一种定义明确的方法来完成您的要求

如果我说:

x = object()
x是“原子可酸洗的”吗?如果我说:

x.foo = threading.Lock()
??x现在是“原子酸洗”吗


如果我创建了一个始终具有lock属性的单独类,该怎么办?如果我从实例中删除了该属性会怎么样?

鉴于您愿意破坏封装,我认为这是您能做的最好的事情:

from pickle import Pickler
import os

class AtomicPickler(Pickler):
  def __init__(self, protocol):
    # You may want to replace this with a fake file object that just
    # discards writes.
    blackhole = open(os.devnull, 'w')

    Pickler.__init__(self, blackhole, protocol)
    self.depth = 0

  def save(self, o):
    self.depth += 1
    if self.depth == 1:
      return Pickler.save(self, o)
    self.depth -= 1
    return

def is_atomically_pickleable(o, protocol=None):
  pickler = AtomicPickler(protocol)
  try:
    pickler.dump(o)
    return True
  except:
    # Hopefully this exception was actually caused by dump(), and not
    # something like a KeyboardInterrupt
    return False
在Python中,判断某个东西是否有效的唯一方法是尝试它。这就是像Python这样动态的语言的本质。你的问题的困难在于,你想区分“顶层”的失败和深层次的失败

Pickler.save
本质上是Python的pickle逻辑的控制中心,因此上面创建了一个修改的
Pickler
,它忽略对其
save
方法的递归调用。在顶级保存中引发的任何异常都将被视为酸洗失败。您可能需要在
语句中添加限定符,但
语句除外。Python中不合格的
异常通常是个坏主意,因为异常不仅用于程序错误,还用于
键盘中断和
SystemExit

对于具有奇数定制酸洗逻辑的类型,这可能会给出可以说是错误否定的结果。例如,如果您创建了一个自定义列表类,该类不是递归调用
Pickler.save
,而是以某种方式尝试自行pickle其元素,然后创建了该类的实例,其中包含其自定义逻辑无法pickle的元素,
在原子上是可pickle的
对于该实例将返回
False
,即使删除有问题的元素将导致可pickle的对象


另外,请注意,
的协议参数在原子上是可pickle的
。从理论上讲,当使用不同的协议进行pickle处理时,对象的行为可能会有所不同(尽管这会很奇怪),因此您应该使它与您提供给
dump

的协议参数相匹配。我认为持久化id接口与您尝试执行的操作不匹配。它设计用于对象引用新程序上的等效对象而不是旧程序的副本时。您正试图过滤掉每个不能被pickle的对象,这些对象是不同的,为什么要尝试这样做

我认为这是代码中存在问题的迹象。您想要pickle引用gui小部件、文件和锁的对象这一事实表明您正在做一些奇怪的事情。通常持久化的对象类型不应与该类型的对象相关或包含对该类型对象的引用

话虽如此,我认为你最好的选择是:

class MyPickler(Pickler):
    def save(self, obj):
        try:
             Pickler.save(self, obj)
        except PicklingEror:
             Pickle.save( self, FilteredObject(obj) )
这应该适用于python实现,我不保证在C实现中会发生什么。保存的每个对象都将传递给save方法。当无法pickle对象时,此方法将引发PicklingError。此时,您可以介入并调用函数,要求它对您自己的对象进行pickle,该对象应该可以进行pickle

编辑

据我所知,您基本上拥有一个用户创建的对象字典。有些对象是可拾取的,有些则不是。我会这样做:

class saveable_dict(dict):
    def __getstate__(self):
        data = {}
        for key, value in self.items():
             try:
                  encoded = cPickle.dumps(value)
             except PicklingError:
                  encoded = cPickle.dumps( Unpickable() )
        return data

    def __setstate__(self, state):
       for key, value in state:
           self[key] = cPickle.loads(value)
然后,当您想要保存对象集合时,请使用该字典。用户应该能够取回任何可拾取的对象,但其他所有对象都将作为Unpicklable()对象返回。这种方法与前面的方法的区别在于对象本身是可拾取的,但引用了不可拾取的对象。但不管怎样,这些物体很可能会破碎


这种方法还有一个好处,即它完全保留在已定义的API中,因此可以在cPickle或pickle中工作。

我最终为此编写了自己的解决方案

。它是的一部分,因此您可以使用它并从garlicsim.general\u misc import pickle\u tools
执行

如果要在Python 3代码上使用它,请使用

以下是该模块的摘录(可能已过时):


dill
具有用于此类检查的
pickles
方法

>>> import threading
>>> l = [threading.Lock()]
>>> 
>>> import dill
>>> dill.pickles(l)
True
>>> 
>>> dill.pickles(threading.Lock())
True
>>> f = open('whatever', 'w') 
>>> f.close()
>>> dill.pickles(open('whatever', 'r'))
True
嗯,
dill
原子化地对所有示例进行pickle,所以让我们尝试其他方法:

>>> l = [iter([1,2,3]), xrange(5)]
>>> dill.pickles(l)
False
好的,这失败了。现在,让我们调查一下:

>>> dill.detect.trace(True)
>>> dill.pickles(l)
T4: <type 'listiterator'>
False
>>> map(dill.pickles, l)
T4: <type 'listiterator'>
Si: xrange(5)
F2: <function _eval_repr at 0x106991cf8>
[False, True]

现在我们的对象原子化了。

不可能。“原子上可酸洗”是不明确的——你不能用这种方式定义它。我认为你应该详细说明你实际上在做什么。这样我们就可以判断这是否是一个好的解决方案。(现在看来这是一个奇怪的请求)。如果不是,我们可以提出一个更好的解决方案。@Winston Ewert:这是其中的一个子任务:我正在遵循获胜的解决方案,我希望my
persistent_id
函数有一个更好的实现。好的,在你明白不能pickle say文件之后,你打算怎么做?你必须返回一些用作持久id的内容。我看不出将对象标识为不可粘贴的内容对你有什么帮助。我将只放置一些
FilteredObject
内容,而不是文件,说“这里有一个file对象,但它不可能是p
>>> dill.detect.trace(True)
>>> dill.pickles(l)
T4: <type 'listiterator'>
False
>>> map(dill.pickles, l)
T4: <type 'listiterator'>
Si: xrange(5)
F2: <function _eval_repr at 0x106991cf8>
[False, True]
>>> l[0] = xrange(1,4)
>>> dill.pickles(l)
Si: xrange(1, 4)
F2: <function _eval_repr at 0x106991cf8>
Si: xrange(5)
True