更改模块目录后 Python 进行 pickling

2025-04-16 08:57:00
admin
原创
16
摘要:问题描述:我最近更改了程序的目录布局:之前,我把所有模块都放在“main”文件夹中。现在,我把它们移动到了一个以程序名称命名的目录中,并__init__.py在那里创建了一个包。现在我的主目录中有一个单独的 .py 文件,用于启动我的程序,这更加整洁。无论如何,尝试从我程序的先前版本中加载pickle文件失败...

问题描述:

我最近更改了程序的目录布局:之前,我把所有模块都放在“main”文件夹中。现在,我把它们移动到了一个以程序名称命名的目录中,并__init__.py在那里创建了一个包。

现在我的主目录中有一个单独的 .py 文件,用于启动我的程序,这更加整洁。

无论如何,尝试从我程序的先前版本中加载pickle文件失败了。我收到“ImportError: No module named tools”的错误信息——我猜是因为我的模块之前在主文件夹中,现在在whyteboard.tools中,而不是普通的tools。但是,导入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。在目录重新整理之前,这个 pickling 问题肯定不会发生。


解决方案 1:

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

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

whyteboard.tools不是“与...相同的模块” (尽管它可以被同一个包中的其他模块导入,但最终还是会以...结尾:这绝对至关重要,否则同一个包中模块与另一个包中的模块导入的相同模块最终会出现多个甚至可能冲突的条目!)。tools`import toolssys.modulessys.modules['whyteboard.tools']`

如果您的 pickle 文件采用良好/高级格式(而不是出于兼容性原因而默认使用的旧式 ascii 格式),那么在执行此类更改后迁移它们实际上可能并不sys.modules像“编辑文件”(二进制文件等等)那么简单,尽管另一个答案建议这样做。我建议您编写一个小的“pickle 迁移脚本”:让它像这样修补……:

import sys
from whyteboard import tools

sys.modules['tools'] = tools

然后将cPickle.load每个文件del sys.modules['tools']cPickle.dump每个加载的对象返回到文件:临时的额外条目sys.modules应该让泡菜成功加载,然后再次转储它们应该使用实例类的正确模块名称(删除额外的条目应该确保这一点)。

解决方案 2:

这可以通过使用以下自定义“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)

那么您需要使用renamed_load()instead ofpickle.load()renamed_loads()instead of pickle.loads()

解决方案 3:

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

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

解决方案 4:

pickle通过引用序列化类,因此即使你更改了类的路径,也无法进行 unpickle 操作,因为找不到该类。如果使用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

解决方案 5:

这是 pickle 的正常行为,unpickled 对象需要有其定义模块可导入。

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

解决方案 6:

对于像我这样需要更新大量 pickle 转储的人来说,这里有一个实现@Alex Martelli 的优秀建议的函数:

import sys
from types import ModuleType
import pickle

# import torch

def update_module_path_in_pickled_object(
    pickle_path: str, old_module_path: str, new_module: ModuleType
) -> None:
    """Update a python module's dotted path in a pickle dump if the
    corresponding file was renamed.

    Implements the advice in https://stackoverflow.com/a/2121918.

    Args:
        pickle_path (str): Path to the pickled object.
        old_module_path (str): The old.dotted.path.to.renamed.module.
        new_module (ModuleType): from new.location import module.
    """
    sys.modules[old_module_path] = new_module

    dic = pickle.load(open(pickle_path, "rb"))
    # dic = torch.load(pickle_path, map_location="cpu")

    del sys.modules[old_module_path]

    pickle.dump(dic, open(pickle_path, "wb"))
    # torch.save(dic, pickle_path)

就我而言,这些转储是 PyTorch 模型检查点。因此注释掉了torch.load/save()

例子

from new.location import new_module

for pickle_path in ('foo.pkl', 'bar.pkl'):
    update_module_path_in_pickled_object(
        pickle_path, "old.module.dotted.path", new_module
    )

解决方案 7:

当你尝试加载包含类引用的 pickle 文件时,必须遵循保存 pickle 时的结构。如果你想在其他地方使用 pickle,则必须指明这个类或其他对象的位置;所以,按照下面的方法操作可以节省时间:

import sys
sys.path.append('path/to/folder containing the python module')

解决方案 8:

我知道这已经有一段时间了,但这为我解决了这个问题:

本质上,使用完整的导入路径(例如concurrent.run_concurrent),而不仅仅是模块名称(例如run_concurrent


共享代码:

import importlib
module_path="concurrent.run_concurrent"

...

module = importlib.util.module_from_spec(spec)

原文(不好):

module_name = module_path.split(".")[-1]

spec = importlib.util.spec_from_file_location(module_name, filepath)

...

sys.modules[module_name] = module

替换为以下内容(删除所有对 的引用module_name):

# Remove "module_name"

# Use "module_path" instead of "module_name"
spec = importlib.util.spec_from_file_location(module_path, filepath)

...

# Use "module_path" instead of "module_name"
sys.modules[module_path] = module

解决方案 9:

根据这个答案实现,下面的版本使用字典来支持保存泡菜后的多个模块重命名:

import pickle

class UnpicklerRM(pickle.Unpickler):

    modNameMap = {
        "savedModelName"    : "newMadelName",
        #...
    }

    def find_class(self, moduleName:str, objName:str):
        if moduleName in self.modNameMap:
            moduleName = self.modNameMap[moduleName]
        return super().find_class(moduleName, objName)


#read pickle using module name changed after saving
with open('fname.pickle', 'rb') as f:
    data = UnpicklerRM(f).load()


#read pickle using module name when saving
with open('fname.pickle', 'rb') as f:
    data = pickle.load(f)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用