1. ホーム
  2. パイソン

[解決済み】オブジェクトの保存 (データ永続化)

2022-03-28 14:56:51

質問

このようなオブジェクトを作成しました。

company1.name = 'banana' 
company1.value = 40

このオブジェクトを保存したいのですが。どうすればいいでしょうか?

どのように解決するのですか?

を使用することができます。 pickle というモジュールが標準ライブラリに含まれています。 ここでは、その初歩的な応用例を紹介します。

import pickle

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

with open('company_data.pkl', 'wb') as outp:
    company1 = Company('banana', 40)
    pickle.dump(company1, outp, pickle.HIGHEST_PROTOCOL)

    company2 = Company('spam', 42)
    pickle.dump(company2, outp, pickle.HIGHEST_PROTOCOL)

del company1
del company2

with open('company_data.pkl', 'rb') as inp:
    company1 = pickle.load(inp)
    print(company1.name)  # -> banana
    print(company1.value)  # -> 40

    company2 = pickle.load(inp)
    print(company2.name) # -> spam
    print(company2.value)  # -> 42

また、次のような独自の簡単なユーティリティを定義して、ファイルを開き、そこに1つのオブジェクトを書き込むこともできます。

def save_object(obj, filename):
    with open(filename, 'wb') as outp:  # Overwrites any existing file.
        pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

# sample usage
save_object(company1, 'company1.pkl')

更新情報

この回答はとても人気があるので、少し高度な使用法について触れてみたいと思います。

cPickle (または _pickle ) 対 pickle

を実際に使用することが望ましい場合がほとんどです。 cPickle モジュールではなく pickle というのも、前者はC言語で書かれており、はるかに高速だからです。両者には微妙な違いがありますが、ほとんどの場面で同等であり、C言語版の方がはるかに優れた性能を発揮します。C言語版に切り替えるのはこれ以上ないほど簡単です。 import ステートメントをこれに変更します。

import cPickle as pickle

Python 3では cPickle という名前に変更されました。 _pickle を使用する必要がなくなりました。 pickle モジュールが自動的に行うようになりました。 Python 3でpickleと_pickleの違いは何ですか? .

要するに、以下のようなものを使って、あなたのコードが確実に 常に は、Python 2 と 3 の両方で利用可能な場合は、C バージョンが使用されます。

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

データストリームのフォーマット(プロトコル)

pickle と呼ばれるPython固有のフォーマットでファイルの読み書きができます。 プロトコル で説明されているように ドキュメント プロトコルバージョン0"はASCIIであるため、人間が読むことが可能です。バージョン 0 はバイナリで、使用されている Python のバージョンにより、最も高いものが使用できます。デフォルトもPythonのバージョンに依存します。Python 2 では、デフォルトはプロトコルバージョンでした。 0 しかし、Python 3.8.1では、プロトコルバージョン 4 . Python 3.x では、このモジュールには pickle.DEFAULT_PROTOCOL が追加されていますが、これはPython 2には存在しません。

幸いなことに pickle.HIGHEST_PROTOCOL を使うだけです。 -1 - 負のインデックスを使用してシーケンスの最後の要素を参照するのと同じです。 つまり、代わりにこう書きます。

pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

書けばいいんだよ。

pickle.dump(obj, outp, -1)

いずれにせよ、プロトコルを一度だけ指定する必要があるのは Pickler オブジェクトを複数のpickle操作で使用することができます。

pickler = pickle.Pickler(outp, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

備考 : 異なるバージョンのPythonが動作している環境では、おそらくすべてのPythonが読める特定のプロトコル番号を明示的に使用(つまりハードコード)したいでしょう(後のバージョンでは、一般的に以前のバージョンで生成されたファイルを読むことができます)。

複数のオブジェクト

pickleファイル中 できること は、上記のサンプルのように任意の数のpickleオブジェクトを含みますが、未知の数のpickleオブジェクトがある場合、多くの場合、それらをすべて list , tuple または dict を一回の呼び出しでファイルに書き込む。

tech_companies = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')

でリストとその中のすべてを後で復元します。

with open('tech_companies.pkl', 'rb') as inp:
    tech_companies = pickle.load(inp)

主な利点は、後でロードし直すために保存されているオブジェクトインスタンスの数を知る必要がないことです(ただし、その情報なしでロードする場合は 可能ですが、少し特殊なコードが必要です)。関連する質問に対する回答を参照してください。 pickleファイルに複数のオブジェクトを保存して読み込む? をご覧ください。個人的には、@Lutz Prechelt の 回答 が一番良いので、以下のサンプルコードではその方法を用いています。

class Company:
    def __init__(self, name, value):
        self.name = name
        self.value = value

def pickle_loader(filename):
    """ Deserialize a file of pickled objects. """
    with open(filename, "rb") as f:
        while True:
            try:
                yield pickle.load(f)
            except EOFError:
                break

print('Companies in pickle file:')
for company in pickle_loader('company_data.pkl'):
    print('  name: {}, value: {}'.format(company.name, company.value))