我最近改变了程序的目录布局:之前,我将所有模块都放在“main”文件夹中。现在,我已将它们移动到以该程序命名的目录中,并在其中放置__init__.py
来制作包。
现在我的主目录中有一个.py文件,用于启动我的程序,这个文件非常简洁。
无论如何,尝试加载以前版本的程序中的pickle文件是失败的。我得到了,“ImportError:没有模块命名工具” - 我想这是因为我的模块以前在主文件夹中,现在它在whyteboard.tools中,而不仅仅是简单的工具。但是,在工具模块中导入的代码与它位于同一目录中,因此我怀疑是否需要指定包。
所以,我的程序目录看起来像这样:
whyteboard-0.39.4
-->whyteboard.py
-->README.txt
-->CHANGELOG.txt
---->whyteboard/
---->whyteboard/__init__.py
---->whyteboard/gui.py
---->whyteboard/tools.py
whyteboard.py从whyteboard / gui.py启动一段代码,启动GUI。在目录重新组织之前,肯定没有发生这种酸洗问题。
答案 0 :(得分:64)
正如pickle's docs所说,为了保存和恢复类实例(实际上也是一个函数),你必须尊重某些约束:
pickle可以保存和恢复类 然而,实例是透明的 类定义必须是可导入的 和住在同一模块中 对象存储
whyteboard.tools
不“与”tools
相同的模块(即使它可以由import tools
由同一个包中的其他模块导入,它最终在sys.modules
中作为sys.modules['whyteboard.tools']
:这绝对是至关重要的,否则同一个包中的一个模块导入的相同模块与另一个包中的模块导入的模块最终将会出现多个且可能存在冲突的条目!)。< / p>
如果您的pickle文件采用良好/高级格式(与旧的ascii格式相反,仅出于兼容性原因而是默认格式),在执行此类更改后迁移它们实际上可能不尽管另外一个答案表明,“编辑文件”(二进制&amp; c ......!)非常简单。相反,我建议您制作一个“pickle-migrating script”:让它像这样修补sys.modules
......:
import sys
from whyteboard import tools
sys.modules['tools'] = tools
然后cPickle.load
每个文件,del sys.modules['tools']
和cPickle.dump
每个加载的对象返回文件:sys.modules
中的临时额外条目应该让pickle成功加载,然后再次转储它们应该为实例的类使用正确的module-name(删除那个额外的条目应该确保这一点)。
答案 1 :(得分:10)
发生在我身上,通过在加载pickle之前将模块的新位置添加到sys.path来解决它:
import sys
sys.path.append('path/to/whiteboard')
f = open("pickled_file", "rb")
pickle.load(f)
答案 2 :(得分:5)
这是pickle的正常行为,unpickled对象需要有defining module importable。
您应该可以通过编辑pickle文件来更改模块路径(即从tools
更改为whyteboard.tools
),因为它们通常是简单的文本文件。
答案 3 :(得分:5)
pickle
通过引用序列化类,因此如果您更改了类的生命,它将不会因为找不到类而无法解开。如果使用dill
而不是pickle
,则可以通过引用或直接序列化类(通过直接序列化类而不是它的导入路径)。只需在dump
之后和load
之前更改类定义,即可轻松模拟这一点。
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>>
>>> class Foo(object):
... def bar(self):
... return 5
...
>>> f = Foo()
>>>
>>> _f = dill.dumps(f)
>>>
>>> class Foo(object):
... def bar(self, x):
... return x
...
>>> g = Foo()
>>> f_ = dill.loads(_f)
>>> f_.bar()
5
>>> g.bar(4)
4
答案 4 :(得分:5)
这可以通过使用find_class()
的自定义“ unpickler”来完成:
import io
import pickle
class RenameUnpickler(pickle.Unpickler):
def find_class(self, module, name):
renamed_module = module
if module == "tools":
renamed_module = "whyteboard.tools"
return super(RenameUnpickler, self).find_class(renamed_module, name)
def renamed_load(file_obj):
return RenameUnpickler(file_obj).load()
def renamed_loads(pickled_bytes):
file_obj = io.BytesIO(pickled_bytes)
return renamed_load(file_obj)
然后,您需要使用renamed_load()
代替pickle.load()
,并使用renamed_loads()
代替pickle.loads()
。