web-dev-qa-db-ja.com

namedtupleインスタンスを正しくpickleする方法

ピクルスの使い方を学んでいます。 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()
44
Dirty Penguin

関数の名前付きタプル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_を見つけることです。ご使用のバージョンでは、Ppickle_test()関数のlocalであり、イントロスペクトまたはインポートはできません。

namedtuple()はクラスファクトリであることを覚えておくことが重要です。パラメータを指定すると、インスタンスを作成するためのクラスオブジェクトが返されます。 pickleは、インスタンスに含まれるdataに加えて、インスタンスを再構築するための元のクラスへの文字列参照のみを保存します。

66
Martijn Pieters

私の質問をコメントとしてメインの回答に追加した後、動的に作成された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__の別のメンバーを上書きする可能性があることに注意してください。

8
Chuim

別のスレッドで この回答 を見つけました。これはすべて、名前付きタプルの命名に関するものです。これは私のために働いた:

group_t =            namedtuple('group_t', 'field1, field2')  # this will work
mismatched_group_t = namedtuple('group_t', 'field1, field2')  # this will throw the error
5
Ruvalcaba

または、 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")
3
Peque

ここでの問題は、子プロセスがオブジェクトのクラス(この場合はクラスP)をインポートできないことです。マルチモデルプロジェクトの場合、子プロセスが使用される場所であればどこでもクラスPをインポートできる必要があります。

簡単な回避策は、globals()に影響を与えてインポート可能にすることです。

globals()["P"] = P
0