【问题标题】:pickle load error "__init__() takes exactly 2 arguments (1 given)"pickle 加载错误“__init__() 恰好需要 2 个参数(给定 1 个)”
【发布时间】:2013-01-09 15:18:16
【问题描述】:

我的问题是自定义类已使用 pickle.dump 保存,因为这些文件已保存自定义类已更改,现在当我使用 pickle.load 时出现此错误。是不是保存的文件有问题?

错误:

File "/cprprod/extern/lib/python2.7/pickle.py", line 1378, in load
return Unpickler(file).load()
File "/cprprod/extern/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
file "/cprprod/extern/lib/python2.7/pickle.py", line 1070, in load_inst
self._instantiate(klass, self.marker())
File "/cprprod/extern/lib/python2.7/pickle.py", line 1060, in _instantiate
value = klass(*args)

我可以做些什么来加载文件吗?

代码

file = open(filename,'rb')
obj = pickle.load(file)

会给我错误。


这是一些可以重现错误的最小代码:

import pickle

class foo:
    def __init__(self,a):
        self.a = a

    def __str__(self):
        return str(self.a)

obj = foo(1)

with open('junk','wb') as f:
    pickle.dump(obj,f)

class foo:
    def __init__(self,a,b):
        self.a = a
        self.b = b
    def __str__(self):
        return '%s %s'%(self.a,self.b)

    def __getinitargs__(self):
        return (self.a,self.b)

with open('junk','rb') as f:
    obj = pickle.load(f)
    print str(obj)

【问题讨论】:

  • 你当前版本的类期望(在 init 内)与实例化对象的参数是什么,你能举一些例子吗?
  • 您的班级是否实现了__getinitargs__()__getnewargs__()(至少在它被腌制时)?
  • 这里有些可疑。 __init__shouldn't be called。你的课是旧式的还是新式的?另外,你腌制了一个class还是一个class instance?你的班级有__getinitargs__ 方法吗?
  • @MartijnPieters - 显然我什至无法比你更快地让我的 cmets 进入:p
  • @mgilson:这是在发布时我是出租车! :-P

标签: python pickle


【解决方案1】:

鉴于我代表您在问题中发布的人为代码,我们可以将这个错误“修复”为:

with open('junk','rb') as f:
    try:
        obj = pickle.load(f)
    except Exception as e:
        print e
        position = f.tell()
        a = foo.__getinitargs__
        del foo.__getinitargs__
        f.seek(position)
        obj = pickle.load(f)
        foo.__getinitargs__ = a

    print str(obj)

现在我们看到该实例已被取消腌制并且不再具有属性b

【讨论】:

  • 是的,正在针对您的示例更新再次对此进行测试,它确实有效!
  • 事实上,我的初始方法也有效,而且在我看来没有那么难懂。扩展为说明这两种方法都解决了 OP 问题。
  • @MartijnPieters -- 是的,因此我对你的回答的评论“虽然这个 [你的回答] 可能是一个更好的方法”
【解决方案2】:

如果您添加了__getinitargs__(),那么您需要确保您的新类可以处理传递给__init__() 的参数。没有 __getinitargs__ 数据的旧数据仍会导致调用 __init__ 但带有 no 参数。

通过关键字参数将__init__ 的参数设为可选:

def __init__(self, otherarg=None):
    if otherarg is None:
        # created from an old-revision pickle. Handle separately.
        # The pickle will be loaded *normally* and data will still be set normally
        return
    self.otherarg = otherarg

加载旧式泡菜时,这些类的数据仍将恢复。您可以根据需要使用__setstate__() 转换内部状态。

或者,暂时删除类中的__getinitargs__方法:

initargs = foo.__getinitargs__.__func__
del foo.__getinitargs__
obj = pickle.load(f)
foo.__getinitargs__ = initargs

并从现在加载的对象中重新转储您的泡菜,并恢复__getinitargs__

我已经测试了这两种方法,在这两种情况下,旧数据都被正确加载,然后您可以再次将您的对象转储到一个新的 pickle 文件 __getinitargs__ 就好了。

【讨论】:

  • 我的问题是以前版本的类已经被腌制了,我正在尝试加载这些文件。
  • @wDroter:这就是我们在这里试图帮助你的。
  • 我想知道是否可以a = klass.__getinitargs__;del klass.__getinitargs__;pickle.load();klass.__getinitargs__ = a。换句话说,删除__getinitargs__,unpickle 然后猴子补丁__getinitargs__ 重新打开...(尽管这可能是一个更好的方法)
  • @mgilson:问题是,我正在尝试,但我无法在这里重现 OP 问题。我怀疑,只有当腌制数据实际上有空的 __getinitargs__ 腌制值时才会发生这种情况。在这种情况下,数据无论如何都是无用的。
  • @MartijnPieters -- 您是否尝试过我代表 OP 发布的代码来重现问题?它仅适用于 old 样式类,因此请确保您使用的是 python2.x
【解决方案3】:

您可能希望修改自定义类以选择性地需要第二个参数。这将保持与您的腌制对象的奖励兼容性。

【讨论】:

  • 谢谢,但是这些已经被腌制为该课程的先前版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
  • 2015-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多