更改模块目录后的Python酸洗

时间:2010-01-23 02:52:40

标签: python pickle

我最近改变了程序的目录布局:之前,我将所有模块都放在“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。在目录重新组织之前,肯定没有发生这种酸洗问题。

5 个答案:

答案 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()