更改模块目录后的Python酸洗

Ste*_*oat 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.在目录重新组织之前,肯定没有发生这种酸洗问题.

Ale*_*lli 71

正如pickle的文档所说,为了保存和恢复类实例(实际上也是一个函数),你必须尊重某些约束:

pickle可以透明地保存和恢复类实例,但是类定义必须是可导入的,并且存在于与存储对象时相同的模块中

whyteboard.tools与"相同的模块为" tools(即使它可通过被导入import tools通过在同一包装中的其它模块,则在结束了sys.modulessys.modules['whyteboard.tools']:这是绝对重要的,否则由一个在同一封装中导入VS在同一个模块另一个包最终会有多个可能相互冲突的条目!).

如果您的咸菜文件是一个好/高级格式(相对于旧的ASCII格式是默认仅出于兼容性的原因),迁移过一次执行这样的变化可能实际上没有相当的"编辑文件"小巫见大巫(这是二元&c ...!),尽管另一个答案表明.相反,我建议你制作一个"pickle-migrating script":让它sys.modules像这样修补......:

import sys
from whyteboard import tools

sys.modules['tools'] = tools
Run Code Online (Sandbox Code Playgroud)

然后cPickle.load每个文件,del sys.modules['tools']以及cPickle.dump每个加载的对象返回文件:临时额外的条目sys.modules应该让pickle成功加载,然后再次转储它们应该为实例的类使用正确的模块名称(删除那个额外的条目应该使肯定的.)

  • 如果这对其他人有帮助 - 在我的例子中,它移动到了不同的包,但在层次结构内,因此它从“my_old_module.h1.h2”移动到“my_new_module.h1.h2”,所以我必须覆盖“my_old_module”指向“my_new_module”,也指向“my_old_module.h1.h2”以指向“my_new_module.h1.h2” (2认同)

Ran*_*nch 13

发生在我身上,通过在加载pickle之前将模块的新位置添加到sys.path来解决它:

import sys
sys.path.append('path/to/whiteboard')
f = open("pickled_file", "rb")
pickle.load(f)
Run Code Online (Sandbox Code Playgroud)


bos*_*ter 11

这可以通过使用一个自定义“ unpickler”来完成find_class()

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)
Run Code Online (Sandbox Code Playgroud)

然后,您需要使用renamed_load()代替pickle.load()renamed_loads()代替pickle.loads()


Mik*_*rns 8

pickle通过引用序列化类,因此如果您更改了类的生命,它将不会解开,因为将找不到该类.如果使用dill而不是pickle,则可以通过引用或直接序列化类(通过直接序列化类而不是它的导入路径).只需在a 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
Run Code Online (Sandbox Code Playgroud)

  • @bariod:是的,实际上...现在,您需要在对loads的调用中使用ignore = True来使用存储的类。如果不存在,它会引用main中定义的任何“ Foo”(如果存在的话)(如果main中不存在“ Foo”,则仅使用存储的“ Foo”)。 (2认同)

Lup*_*uch 5

这是腌制的正常行为,未腌制的对象需要具有其定义模块importable

您应该能够通过编辑腌制的文件来更改模块路径(即从toolswhyteboard.tools),因为它们通常是简单的文本文件。