25

Project A同僚が作成したプロジェクトを別の python プロジェクトに統合しようとしています。現在、この同僚はコードで相対インポートを使用していませんが、代わりに使用しています

from packageA.moduleA import ClassA
from packageA.moduleA import ClassB

その結果、クラスを でピクルスにしましたcPickleProject Aきちんとするために、彼の ( ) が私のプロジェクト内に構築したパッケージを非表示にしたいと思います。ただし、これにより、 で定義されたクラスのパスが変更されpackageAます。問題ありません。次を使用してインポートを再定義します

from ..packageA.moduleA import ClassA
from ..packageA.moduleA import ClassB

しかし、クラスの un pickle 化は次のメッセージで失敗します

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

cPickleでは、モジュールの定義が明らかに見えないのはなぜですか。packageAシステム パスにルートを追加する必要がありますか? これは問題を解決する正しい方法ですか?

ファイルは次のcPickledようになります

ccopy_reg
_reconstructor
p1
(cpackageA.moduleA
ClassA
p2
c__builtin__
object
p3
NtRp4

古いプロジェクト階層は一種のものです

packageA/
    __init__.py
    moduleA.py
    moduleB.py
packageB/
    __init__.py
    moduleC.py
    moduleD.py

そのすべてを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
4

4 に答える 4

28

ピクルスのインポートを機能させるには、エイリアスを作成する必要があります。パッケージの__init__.pyファイルには次のようになります。WrapperPackage

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

ただし、追加のエントリを作成する必要がある場合があります。

sys.modules['packageA.moduleA'] = moduleA
# etc.

packageA.moduleAこれで、cPickleはpackageA.moduleB古い場所を見つけます。

後でpickleファイルを書き直したい場合は、その時点で新しいモジュールの場所が使用されます。cPickle上で作成された追加のエイリアスは、問題のモジュールがクラスを再度作成するときに取得するための新しい場所の名前を持っていることを確認する必要があります。

于 2012-11-15T13:41:33.583 に答える
5

@MartinPietersの回答に加えて、これを行うもう1つの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'

pickleとの両方のプロセスの詳細については、cPickleこちらを参照してください

于 2012-11-15T20:34:27.613 に答える
2

考えられる解決策の 1 つは、pickle ファイルを直接編集することです (アクセスできる場合)。モジュールパスの変更という同じ問題に遭遇し、ファイルを pickle.HIGHEST_PROTOCOL として保存したため、理論的にはバイナリである必要がありますが、モジュールパスはピクルファイルの先頭にプレーンテキストで配置されていました。そのため、古いモジュール パスのすべてのインスタンスを新しいモジュール パスで検索置換しただけで、正しくロードされました。

特に非常に複雑なピクルされたオブジェクトがある場合、このソリューションは万人向けではないと確信していますが、私にとっては迅速でダーティなデータ修正でした!

于 2014-03-15T20:02:22.857 に答える