PicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.Dec
Posted
技术标签:
【中文标题】PicklingError: Can\'t pickle <class \'decimal.Decimal\'>: it\'s not the same object as decimal.Decimal【英文标题】:PicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.DecimalPicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.Decimal 【发布时间】:2010-11-27 14:53:42 【问题描述】:这是我今天在 filmmaster.com:
遇到的错误PicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.Decimal
这到底是什么意思?好像没有多大意义…… 它似乎与 django 缓存有关。您可以在此处查看整个回溯:
Traceback (most recent call last): File "/home/filmaster/django-trunk/django/core/handlers/base.py", line 92, in get_response response = callback(request, *callback_args, **callback_kwargs) File "/home/filmaster/film20/film20/core/film_views.py", line 193, in show_film workflow.set_data_for_authenticated_user() File "/home/filmaster/film20/film20/core/film_views.py", line 518, in set_data_for_authenticated_user object_id = self.the_film.parent.id) File "/home/filmaster/film20/film20/core/film_helper.py", line 179, in get_others_ratings set_cache(CACHE_OTHERS_RATINGS, str(object_id) + "_" + str(user_id), userratings) File "/home/filmaster/film20/film20/utils/cache_helper.py", line 80, in set_cache return cache.set(CACHE_MIDDLEWARE_KEY_PREFIX + full_path, result, get_time(cache_string)) File "/home/filmaster/django-trunk/django/core/cache/backends/memcached.py", line 37, in set self._cache.set(smart_str(key), value, timeout or self.default_timeout) File "/usr/lib/python2.5/site-packages/cmemcache.py", line 128, in set val, flags = self._convert(val) File "/usr/lib/python2.5/site-packages/cmemcache.py", line 112, in _convert val = pickle.dumps(val, 2) PicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.Decimal
电影大师的源代码可以从这里下载:bitbucket.org/filmaster/filmaster-test
任何帮助将不胜感激。
【问题讨论】:
在为对象编写错误的 getstate 方法以更改其泡菜行为后,我遇到了类似的错误。不确定是什么问题,但请检查其中任何一个。 我也看到了这个类装饰器,特别是 Six.add_metaclass 【参考方案1】:在 jupyter notebook 中运行时出现此错误。我认为问题在于我使用的是%load_ext autoreload
autoreload 2
。重新启动我的内核并重新运行解决了这个问题。
【讨论】:
似乎改变类方法是问题的原因。我的猜测是autoreload
没有更新保存在其他地方的定义。重新启动会修复它,因为两个地方都加载了新的定义。
对于这种情况是否有任何其他解决方法无需重新启动内核(这与autoreload
扩展的目的不符..)
@Stav 它确实超越了autoreload
的目的,即重新加载模块而不删除与该模块相关的值,这些值已加载到内存中【参考方案2】:
Pickle 的一个奇怪之处在于,在腌制其中一个实例之前导入类的方式可能会巧妙地改变腌制对象。 Pickle 要求您在 pickle 之前和 unpickle 之前都以相同的方式导入对象。
例如:
from a.b import c
C = c()
pickler.dump(C)
将(有时)制作一个略有不同的对象:
from a import b
C = b.c()
pickler.dump(C)
尝试修改您的导入,它可能会解决问题。
【讨论】:
那么为什么这个酸洗问题只在数千个请求中出现一次并且通常它工作得很好?【参考方案3】:我将在 Python2.7 中用简单的 Python 类演示问题:
In [13]: class A: pass
In [14]: class B: pass
In [15]: A
Out[15]: <class __main__.A at 0x7f4089235738>
In [16]: B
Out[16]: <class __main__.B at 0x7f408939eb48>
In [17]: A.__name__ = "B"
In [18]: pickle.dumps(A)
---------------------------------------------------------------------------
PicklingError: Can't pickle <class __main__.B at 0x7f4089235738>: it's not the same object as __main__.B
显示此错误是因为我们试图转储 A,但因为我们更改了它的名称以引用另一个对象“B”,所以 pickle 实际上与要转储的对象混淆 - 类 A 或 B。显然,pickle 家伙是非常聪明,他们已经检查了这种行为。
解决方案: 检查您尝试转储的对象是否与另一个对象的名称冲突。
我已经用下面的 ipython 和 ipdb 演示了上述案例的调试:
PicklingError: Can't pickle <class __main__.B at 0x7f4089235738>: it's not the same object as __main__.B
In [19]: debug
> /<path to pickle dir>/pickle.py(789)save_global()
787 raise PicklingError(
788 "Can't pickle %r: it's not the same object as %s.%s" %
--> 789 (obj, module, name))
790
791 if self.proto >= 2:
ipdb> pp (obj, module, name) **<------------- you are trying to dump obj which is class A from the pickle.dumps(A) call.**
(<class __main__.B at 0x7f4089235738>, '__main__', 'B')
ipdb> getattr(sys.modules[module], name) **<------------- this is the conflicting definition in the module (__main__ here) with same name ('B' here).**
<class __main__.B at 0x7f408939eb48>
我希望这可以省去一些麻烦!再见!!
【讨论】:
【参考方案4】:我也无法解释为什么这会失败,但我自己的解决方案是更改我的所有代码,而不是这样做
from point import Point
到
import point
这一改变,它奏效了。我很想知道为什么...hth
【讨论】:
这对我也有帮助,我很想知道为什么! 关于它为什么起作用的任何更新?我的猜测是 import XX 会重新加载所有内容,from XX import XXX 只会重新加载特定的模块或函数。【参考方案5】:您是否以某种方式reload(decimal)
或monkeypatch 十进制模块来更改十进制类?这是最有可能产生此类问题的两件事。
【讨论】:
【参考方案6】:调用__init__
以multiprocessing
启动进程可能会出现问题。这是一个演示:
import multiprocessing as mp
class SubProcClass:
def __init__(self, pipe, startloop=False):
self.pipe = pipe
if startloop:
self.do_loop()
def do_loop(self):
while True:
req = self.pipe.recv()
self.pipe.send(req * req)
class ProcessInitTest:
def __init__(self, spawn=False):
if spawn:
mp.set_start_method('spawn')
(self.msg_pipe_child, self.msg_pipe_parent) = mp.Pipe(duplex=True)
def start_process(self):
subproc = SubProcClass(self.msg_pipe_child)
self.trig_proc = mp.Process(target=subproc.do_loop, args=())
self.trig_proc.daemon = True
self.trig_proc.start()
def start_process_fail(self):
self.trig_proc = mp.Process(target=SubProcClass.__init__, args=(self.msg_pipe_child,))
self.trig_proc.daemon = True
self.trig_proc.start()
def do_square(self, num):
# Note: this is an synchronous usage of mp,
# which doesn't make sense. But this is just for demo
self.msg_pipe_parent.send(num)
msg = self.msg_pipe_parent.recv()
print('^2 = '.format(num, msg))
现在,使用上面的代码,如果我们运行这个:
if __name__ == '__main__':
t = ProcessInitTest(spawn=True)
t.start_process_fail()
for i in range(1000):
t.do_square(i)
我们得到这个错误:
Traceback (most recent call last):
File "start_class_process1.py", line 40, in <module>
t.start_process_fail()
File "start_class_process1.py", line 29, in start_process_fail
self.trig_proc.start()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/process.py", line 105, in start
self._popen = self._Popen(self)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/context.py", line 212, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/context.py", line 274, in _Popen
return Popen(process_obj)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/popen_spawn_posix.py", line 33, in __init__
super().__init__(process_obj)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/popen_fork.py", line 21, in __init__
self._launch(process_obj)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/popen_spawn_posix.py", line 48, in _launch
reduction.dump(process_obj, fp)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/reduction.py", line 59, in dump
ForkingPickler(file, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <function SubProcClass.__init__ at 0x10073e510>: it's not the same object as __main__.__init__
如果我们将其更改为使用fork
而不是spawn
:
if __name__ == '__main__':
t = ProcessInitTest(spawn=False)
t.start_process_fail()
for i in range(1000):
t.do_square(i)
我们得到这个错误:
Process Process-1:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/process.py", line 254, in _bootstrap
self.run()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/process.py", line 93, in run
self._target(*self._args, **self._kwargs)
TypeError: __init__() missing 1 required positional argument: 'pipe'
但是如果我们调用start_process
方法,它不会在mp.Process
目标中调用__init__
,像这样:
if __name__ == '__main__':
t = ProcessInitTest(spawn=False)
t.start_process()
for i in range(1000):
t.do_square(i)
它按预期工作(无论我们使用spawn
还是fork
)。
【讨论】:
【参考方案7】:由于基于声誉的限制,我无法发表评论,但Salim Fahedy 的答案和调试路径让我确定了导致此错误的原因,即使使用dill
而不是pickle
:
在后台,dill
也访问了dill
的一些功能。在pickle._Pickler.save_global()
中发生了import
。在我看来,这更像是一种“破解”,而不是真正的解决方案,因为一旦您尝试腌制的实例的类未从该类所在的包的最低级别导入,此方法就会失败。抱歉解释不好,也许例子更合适:
下面的例子会失败:
from oemof import solph
...
(some code here, giving you the object 'es')
...
model = solph.Model(es)
pickle.dump(model, open('file.pickle', 'wb))
它失败了,因为虽然您可以使用 solph.Model
,但该类实际上是 oemof.solph.models.Model
。 save_global()
解决了该问题(或将其传递给 save_global()
之前的某个函数),但随后从 oemof.solph.models
导入 Model
并引发错误,因为它与 from oemof import solph.Model
的导入不同(或类似那个,我不能 100% 确定它的工作原理)。
下面的例子可以工作:
from oemof.solph.models import Model
...
some code here, giving you the object 'es')
...
model = Model(es)
pickle.dump(model, open('file.pickle', 'wb'))
它可以工作,因为现在Model
对象是从同一个地方导入的,pickle._Pickler.save_global()
是从同一个地方导入比较对象 (obj2
)。
长话短说:腌制对象时,请确保从可能的最低级别导入类。
添加:这似乎也适用于存储在您要腌制的类实例的属性中的对象。例如,如果model
有一个属性es
,它本身是oemof.solph.energysystems.EnergySystem
类的对象,我们需要将其导入为:
from oemof.solph.energysystems import EnergySystem
es = EnergySystem()
【讨论】:
很好的答案,很好的解释。【参考方案8】:我的问题是我在一个文件中定义了两次同名的函数。所以我猜它对要腌制哪一个感到困惑。
【讨论】:
【参考方案9】:同样的事情发生在我身上
重启内核对我有用
【讨论】:
【参考方案10】:我在调试 (Spyder) 时遇到了同样的问题。如果运行该程序,一切正常。但是,如果我开始调试,我会遇到 picklingError。
但是,一旦我在每个文件的运行配置(快捷方式:ctrl+F6)中选择了选项在专用控制台中执行,一切都按预期正常工作。我不知道它是如何适应的。
注意:在我的脚本中,我有很多导入,例如
from PyQt5.QtWidgets import *
from PyQt5.Qt import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import os, sys, re, math
我的基本理解是,由于星号 (*),我得到了这个 picklingError。
【讨论】:
【参考方案11】:我遇到了一个还没有人提到的问题。我有一个带有__init__
文件的包,其中包括:
from .mymodule import cls
然后我的***代码说:
import mypkg
obj = mypkg.cls()
问题在于,在我的***代码中,类型似乎是mypkg.cls
,但实际上是mypkg.mymodule.cls
。使用完整路径:
obj = mypkg.mymodule.cls()
避免错误。
【讨论】:
【参考方案12】:我在 Spyder 中遇到了同样的错误。就我而言,结果很简单。我在一个名为“Class”的文件中定义了一个名为“Class”的类。我将定义中的类名称更改为“Class_obj”。 pickle.dump(Class_obj,fileh)
有效,但 pickle.dump(Class,fileh)
在保存在名为“Class”的文件中时无效。
【讨论】:
以上是关于PicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.Dec的主要内容,如果未能解决你的问题,请参考以下文章
PicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.Dec
PicklingError: Can't pickle <function mean_root_squared_error_func at 0x7f2efa224e18>: it's no
pickle.PicklingError: Can't pickle <function past_match_sim at 0x7fa26e03b7b8>: attribute look