pickle load error “__init__() takes exactly 2 arguments (1 given)”

我的未来我决定 提交于 2019-12-10 19:18:26

问题


My issue is that a custom class has been saved with pickle.dump, since these files were saved the custom class has been changed and now when I use pickle.load I am getting this error. Is it a problem with the saved file?

The error:

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)

Is there anything I can do to load the file?

The code

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

will give me the error.


Here is some minimal code which can reproduce the error:

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)

回答1:


Given the contrived code that I posted on your behalf in the question, we can "fix" this error as:

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)

Now we see that the instance has been unpickled and no longer has attribute b.




回答2:


If you added __getinitargs__() then it is up to you to make sure your new class can handle the arguments passed to __init__(). Old data that doesn't have the __getinitargs__ data will still lead to __init__ to be called but with no arguments.

Make the arguments to __init__ optional via keyword arguments:

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

When loading the old-style pickle, the data for these classes will still be restored. You can use __setstate__() to transform the internal state as needed.

Alternatively, temporarily remove the __getinitargs__ method from the class:

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

and re-dump your pickles from the now-loaded objects with __getinitargs__ reinstated.

I've tested both methods and in both cases the old data is loaded correctly and you can then dump your objects again to a new pickle file with __getinitargs__ just fine.




回答3:


You might want to modify the custom class to optionally require a second parameter. This would keep back award compatibility with your pickled objects.



来源:https://stackoverflow.com/questions/14238479/pickle-load-error-init-takes-exactly-2-arguments-1-given

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!