3

MyClassで定義されたクラスがありmy_moduleます。問題のクラスのインスタンスをピクルMyClassするメソッドがあります。pickle_myself

def pickle_myself(self, pkl_file_path):
    with open(pkl_file_path, 'w+') as f:
        pkl.dump(self, f, protocol=2)

にあることを確認しmy_moduleましたPYTHONPATH。インタープリターでは、実行は正常に__import__('my_module')機能します。

>>> __import__('my_module')
<module 'my_module' from 'A:\my_stuff\my_module.pyc'>

ただし、最終的にファイルをロードすると、次のようになります。

File "A:\Anaconda\lib\pickle.py", line 1128, in find_class
  __import__(module)
ImportError: No module named my_module

私が確認したいくつかのこと:


編集 - エラーを再現するおもちゃの例:

例自体は、多数のファイルに分散しています。

まず、モジュールがありますball( というファイルに保存されていますball.py):

class Ball():
    def __init__(self, ball_radius):
        self.ball_radius = ball_radius

    def say_hello(self):
        print "Hi, I'm a ball with radius {}!".format(self.ball_radius)

次に、モジュールがありますtest_environment

import os
import ball
#import dill as pkl
import pickle as pkl

class Environment():
    def __init__(self, store_dir, num_balls, default_ball_radius):
        self.store_dir = store_dir
        self.balls_in_environment = [ball.Ball(default_ball_radius) for x in range(num_balls)]

    def persist(self):
        pkl_file_path = os.path.join(self.store_dir, "test_stored_env.pkl")

        with open(pkl_file_path, 'w+') as f:
            pkl.dump(self, f, protocol=2)

次に、環境を作成し、永続化し、ロードするための関数を含むモジュールを用意しましたmake_persist_load

import os
import test_environment
#import pickle as pkl
import dill as pkl


def make_env_and_persist():
    cwd = os.getcwd()

    my_env = test_environment.Environment(cwd, 5, 5)

    my_env.persist()

def load_env(store_path):
    stored_env = None

    with open(store_path, 'rb') as pkl_f:
        stored_env = pkl.load(pkl_f)

    return stored_env

次に、すべてをまとめるスクリプトを次のように作成しますtest_serialization.py

import os
import make_persist_load

MAKE_AND_PERSIST = True
LOAD = (not MAKE_AND_PERSIST)

cwd = os.getcwd()
store_path = os.path.join(cwd, "test_stored_env.pkl")

if MAKE_AND_PERSIST == True:
    make_persist_load.make_env_and_persist()

if LOAD == True:
    loaded_env = make_persist_load.load_env(store_path)

このおもちゃの例を簡単に使用できるようにするために、すべてを Github リポジトリに配置しました。このリポジトリは、選択したディレクトリに複製するだけで済みます。. 含まれている手順を参照してくださいREADME。これもここに複製します。

指示:

1) リポジトリをディレクトリにクローンします。

2) リポジトリ ディレクトリを PYTHONPATH に追加します。

3) を開きtest_serialization.py、変数MAKE_AND_PERSISTを に設定しますTrue。インタープリターでスクリプトを実行します。

4) 以前のインタプリタ インスタンスを閉じて、新しいインタプリタ インスタンスを起動します。でにtest_serialization.py変更MAKE_AND_PERSISTするFalseと、これはプログラムで に設定さLOADTrueます。スクリプトをインタープリターで実行すると、ImportError: No module named test_environment.

5) デフォルトでは、ピクルスの代わりにディルを使用するようにテストが設定されています。これを変更するには、 および に進みtest_environment.pymake_persist_load.py必要に応じてインポートを変更します。


編集: dill '0.2.5.dev0' に切り替えた後、dill.detect.trace(True)出力

C2: test_environment.Environment
# C2
D2: <dict object at 0x000000000A9BDAE8>
C2: ball.Ball
# C2
D2: <dict object at 0x000000000AA25048>
# D2
D2: <dict object at 0x000000000AA25268>
# D2
D2: <dict object at 0x000000000A9BD598>
# D2
D2: <dict object at 0x000000000A9BD9D8>
# D2
D2: <dict object at 0x000000000A9B0BF8>
# D2
# D2

編集:おもちゃの例は、Mac/Ubuntu (つまり、Unix のようなシステム?) で実行すると完全に機能します。Windows でのみ失敗します。

4

2 に答える 2

4

あなたの質問から、クラスのインスタンスをピクルしようとしているクラス メソッドを使用して、おそらくこのようなことを行っていることがわかります。pkl.dumpそれを行うのはお勧めできません。そうしている場合は、代わりにクラスの外部を使用する方がはるかに適切です(where pklispickleなどdill)。ただし、この設計で機能します。以下を参照してください。

>>> class Thing(object):
...   def pickle_myself(self, pkl_file_path):
...     with open(pkl_file_path, 'w+') as f:
...       pkl.dump(self, f, protocol=2)
... 
>>> import dill as pkl
>>> 
>>> t = Thing()
>>> t.pickle_myself('foo.pkl')

その後、再起動...

Python 2.7.10 (default, Sep  2 2015, 17:36:25) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> f = open('foo.pkl', 'r')
>>> t = dill.load(f)
>>> t
<__main__.Thing object at 0x1060ff410>

はるかに複雑なクラスがあると確信していますが、特にそのクラスが同じディレクトリにある別のファイルを使用している場合は、問題が発生する可能性があります。

>>> import dill
>>> from bar import Zap
>>> print dill.source.getsource(Zap)
class Zap(object):
    x = 1
    def __init__(self, y):
        self.y = y

>>> 
>>> class Thing2(Zap):   
...   def pickle_myself(self, pkl_file_path):
...     with open(pkl_file_path, 'w+') as f:
...       dill.dump(self, f, protocol=2)
... 
>>> t = Thing2(2)
>>> t.pickle_myself('foo2.pkl')

その後、再起動します…</p>

Python 2.7.10 (default, Sep  2 2015, 17:36:25) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> f = open('foo2.pkl', 'r')
>>> t = dill.load(f)
>>> t
<__main__.Thing2 object at 0x10eca8090>
>>> t.y
2
>>> 

ええと… 撃て、それもうまくいきます。コードを投稿する必要があるため、使用しているパターンdill(およびpickle) が失敗するパターンを確認できます。あるモジュールが「インストール」されていない(つまり、ローカルディレクトリにある)別のモジュールをインポートしていることを知っています。

問題を参照してくださいdill: https://github.com/uqfoundation/dill/issues/128 https://github.com/uqfoundation/dill/issues/129 およびこの SO の質問: なぜ dill は参照によって外部クラスをダンプするのですか? いくつかの失敗例と考えられる回避策について。

更新された質問に関する編集:

あなたの問題はわかりません。コマンド ラインからの実行、インタープリターからのインポート ( import test_serialization)、およびインタープリターでのスクリプトの実行 (以下のように、手順 3 ~ 5 に示されています) はすべて機能します。dillそのため、古いバージョンの?を使用している可能性があると思います。

>>> import os
>>> import make_persist_load
>>> 
>>> MAKE_AND_PERSIST = False #True
>>> LOAD = (not MAKE_AND_PERSIST)
>>> 
>>> cwd = os.getcwd()
>>> store_path = os.path.join(cwd, "test_stored_env.pkl")
>>> 
>>> if MAKE_AND_PERSIST == True:
...     make_persist_load.make_env_and_persist()
... 
>>> if LOAD == True:
...     loaded_env = make_persist_load.load_env(store_path)
... 
>>> 

コメントでの議論に基づく編集:

エラーが表示される唯一のOSであるように見えるため、おそらくWindowsの問題のようです。

いくつかの作業後に編集します( https://github.com/uqfoundation/dill/issues/140を参照):

この最小限の例を使用すると、Windows で同じエラーを再現できますが、MacOSX ではまだ動作します…

# test.py
class Environment():
    def __init__(self):
        pass

# doit.py
import test
import dill

env = test.Environment()
path = "test.pkl"
with open(path, 'w+') as f:
    dill.dump(env, f)

with open(path, 'rb') as _f:
    _env = dill.load(_f)
    print _env

ただし、 を使用するopen(path, 'r') as _fと、Windows と MacOSX の両方で動作します。__import__そのため、Windows のは、Windows 以外のシステムよりもファイル タイプに敏感であるように見えます。それでも、 を投げるのImportErrorは奇妙です… しかし、この 1 つの小さな変更でうまくいくはずです。

于 2015-11-28T12:49:20.543 に答える