ピクルスの使い方を学んでいます。 namedtupleオブジェクトを作成してリストに追加し、そのリストをピクルスしようとしました。ただし、次のエラーが表示されます。
pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P
関数内にコードをラップせずにコードを実行すると、完全に機能することがわかりました。関数内にラップされたときにオブジェクトをピクルするために追加のステップが必要ですか?
ここに私のコードがあります:
from collections import namedtuple
import pickle
def pickle_test():
P = namedtuple("P", "one two three four")
my_list = []
abe = P("abraham", "lincoln", "vampire", "hunter")
my_list.append(abe)
f = open('abe.pickle', 'w')
pickle.dump(abe, f)
f.close()
pickle_test()
関数の名前付きタプルoutsideを作成します。
_from collections import namedtuple
import pickle
P = namedtuple("P", "one two three four")
def pickle_test():
my_list = []
abe = P("abraham", "lincoln", "vampire", "hunter")
my_list.append(abe)
f = open('abe.pickle', 'w')
pickle.dump(abe, f)
f.close()
pickle_test()
_
これでpickle
が見つけることができます。現在はグローバルなモジュールです。ピック解除するとき、pickle
モジュールがしなければならないのは、再び___main__.P
_を見つけることです。ご使用のバージョンでは、P
はpickle_test()
関数のlocalであり、イントロスペクトまたはインポートはできません。
namedtuple()
はクラスファクトリであることを覚えておくことが重要です。パラメータを指定すると、インスタンスを作成するためのクラスオブジェクトが返されます。 pickle
は、インスタンスに含まれるdataに加えて、インスタンスを再構築するための元のクラスへの文字列参照のみを保存します。
私の質問をコメントとしてメインの回答に追加した後、動的に作成されたnamedtuple
をピクル可能にする問題を解決する方法を見つけました。実行時(DBクエリ後)にのみフィールドを計算するため、これは私の場合に必要です。
私がやるのはmonkey patch the namedtuple
を__main__
モジュールに効果的に移動することです:
def _CreateNamedOnMain(*args):
import __main__
namedtupleClass = collections.namedtuple(*args)
setattr(__main__, namedtupleClass.__name__, namedtupleClass)
namedtupleClass.__module__ = "__main__"
return namedtupleClass
注意しないと、namedtuple
の名前(args
によって提供される)が__main__
の別のメンバーを上書きする可能性があることに注意してください。
別のスレッドで この回答 を見つけました。これはすべて、名前付きタプルの命名に関するものです。これは私のために働いた:
group_t = namedtuple('group_t', 'field1, field2') # this will work
mismatched_group_t = namedtuple('group_t', 'field1, field2') # this will throw the error
または、 cloudpickle
または dill
をシリアル化に使用できます。
from collections import namedtuple
import cloudpickle
import dill
def dill_test(dynamic_names):
P = namedtuple('P', dynamic_names)
my_list = []
abe = P("abraham", "lincoln", "vampire", "hunter")
my_list.append(abe)
with open('deleteme.cloudpickle', 'wb') as f:
cloudpickle.dump(abe, f)
with open('deleteme.dill', 'wb') as f:
dill.dump(abe, f)
dill_test("one two three four")
ここでの問題は、子プロセスがオブジェクトのクラス(この場合はクラスP)をインポートできないことです。マルチモデルプロジェクトの場合、子プロセスが使用される場所であればどこでもクラスPをインポートできる必要があります。
簡単な回避策は、globals()に影響を与えてインポート可能にすることです。
globals()["P"] = P