使用更改的模块路径取消python对象

Mat*_*yra 18 python import pickle

我正在尝试将Project A同事构建的项目集成到另一个python项目中.现在这位同事没有在他的代码中使用相对导入,而是完成了

from packageA.moduleA import ClassA
from packageA.moduleA import ClassB
Run Code Online (Sandbox Code Playgroud)

然后腌制课程cPickle.为了整洁,我想隐藏他(Project A)在我的项目中构建的包.然而,这会改变定义的类的路径packageA.没问题,我只是重新定义导入使用

from ..packageA.moduleA import ClassA
from ..packageA.moduleA import ClassB
Run Code Online (Sandbox Code Playgroud)

但是现在unle pickle这些类失败并显示以下消息

    with open(fname) as infile: self.clzA = cPickle.load(infile)
ImportError: No module named packageA.moduleA
Run Code Online (Sandbox Code Playgroud)

那么为什么cPickle显然没有看到模块defs.我是否需要添加packageA系统路径的根?这是解决问题的正确方法吗?

cPickled文件看起来像

ccopy_reg
_reconstructor
p1
(cpackageA.moduleA
ClassA
p2
c__builtin__
object
p3
NtRp4
Run Code Online (Sandbox Code Playgroud)

旧的项目层次结构就是那种

packageA/
    __init__.py
    moduleA.py
    moduleB.py
packageB/
    __init__.py
    moduleC.py
    moduleD.py
Run Code Online (Sandbox Code Playgroud)

我想把所有这些都放进去 WrapperPackage

MyPackage/
.. __init__.py
.. myModuleX.py
.. myModuleY.py
WrapperPackage/
.. __init__.py
.. packageA/
   .. __init__.py
   .. moduleA.py
   .. moduleB.py
.. packageB/
   .. __init__.py
   .. moduleC.py
   .. moduleD.py
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 20

您需要为pickle导入创建一个别名才能工作; 以下__init__.pyWrapperPackage包的文件:

from .packageA import * # Ensures that all the modules have been loaded in their new locations *first*.
from . import packageA  # imports WrapperPackage/packageA
import sys
sys.modules['packageA'] = packageA  # creates a packageA entry in sys.modules
Run Code Online (Sandbox Code Playgroud)

您可能需要创建其他条目:

sys.modules['packageA.moduleA'] = moduleA
# etc.
Run Code Online (Sandbox Code Playgroud)

现在cPickle时会发现packageA.moduleA,并packageA.moduleB在自己的老位置一次.

您可能希望之后重新编写pickle文件,此时将使用新模块位置.上面创建的其他别名应该确保有问题的模块具有新的位置名称,cPickle以便在再次编写类时获取.


Mat*_*yra 5

除了@MartinPieters 回答之外,另一种方法是定义类的find_global方法cPickle.Unpickler,或扩展pickle.Unpickler类。

def map_path(mod_name, kls_name):
    if mod_name.startswith('packageA'): # catch all old module names
        mod = __import__('WrapperPackage.%s'%mod_name, fromlist=[mod_name])
        return getattr(mod, kls_name)
    else:
        mod = __import__(mod_name)
        return getattr(mod, kls_name)

import cPickle as pickle
with open('dump.pickle','r') as fh:
    unpickler = pickle.Unpickler(fh)
    unpickler.find_global = map_path
    obj = unpickler.load() # object will now contain the new class path reference

with open('dump-new.pickle','w') as fh:
    pickle.dump(obj, fh) # ClassA will now have a new path in 'dump-new'
Run Code Online (Sandbox Code Playgroud)

两个过程的更详细的解释pickle,并cPickle可以发现在这里