オブジェクトの保存(データの永続化)


233

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

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

このオブジェクトを保存したいと思います。どうやってやるの?


1
ピクルスの使い方の簡単な例については、ここに来る人のを参照してください。
Martin Thoma

@MartinThoma:(リンクされた質問の)承認された回答よりも(おそらく)その回答を好むのはなぜですか?
martineau

私がリンクした時点で、受け入れられた答えは持っていませんでしたprotocol=pickle.HIGHEST_PROTOCOL。私の答えは、漬物の代わりにもなります。
Martin Thoma

回答:


449

pickle標準ライブラリのモジュールを使用できます。これがあなたの例への基本的なアプリケーションです:

import pickle

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

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

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

del company1
del company2

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

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

ファイルを開いてそれに単一のオブジェクトを書き込む次のような独自のシンプルなユーティリティを定義することもできます。

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

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

更新

これは非常に人気のある回答なので、少しだけ高度な使用法のトピックについて触れたいと思います。

cPickle(または_pickle)vspickle

前者はCで記述されており、はるかに高速であるため、実際にcPickleモジュールを使用する方がほとんど常に望ましい方法pickleです。それらの間には微妙な違いがありますが、ほとんどの場合それらは同等であり、Cバージョンは非常に優れたパフォーマンスを提供します。これに切り替えるのは簡単ではありません。importステートメントを次のように変更してください。

import cPickle as pickle

Python 3ではcPickleに名前が変更されました_pickleが、pickleモジュールが自動的に行うようになったため、これを行う必要はなくなりました。Python3のpickleと_pickleの違いを参照してください

要約すると、次のようなものを使用して、Python 2と3の両方で使用可能な場合、コードが常に Cバージョンを使用するようにすることができます。

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

データストリーム形式(プロトコル)

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

幸いにもpickle.HIGHEST_PROTOCOL、すべての呼び出しでそれを書くための省略形があります(それがあなたが望むことであり、通常はそうすることを前提とします)。リテラル番号を使用します-1—負のインデックスを介してシーケンスの最後の要素を参照するのと同じです。だから、書く代わりに:

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

あなたはただ書くことができます:

pickle.dump(obj, output, -1)

どちらの方法でもPickler、複数のピクル操作で使用するオブジェクトを作成した場合は、プロトコルを1回指定するだけです。

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

:異なるバージョンのPythonを実行している環境にいる場合、おそらくすべてのユーザーが読み取ることができる特定のプロトコル番号を明示的に使用(ハードコード)する必要があります(後のバージョンは通常、以前のバージョンで作成されたファイルを読み取ることができます)。 。

複数のオブジェクト

ピクルスファイルをしながらすることができ、それらの未知の数があるとき、上記のサンプルのように漬けたオブジェクトの任意の数を、含まれている、それは、可変サイズのコンテナのいくつかの並べ替えでそれらのすべてを保管のようにする方が簡単だlisttupleまたはdict書き込みそれらすべてを1回の呼び出しでファイルに追加します。

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 input:
    tech_companies = pickle.load(input)

主な利点は、後でロードするために保存されているオブジェクトインスタンスの数を知る必要がないことです(その情報なしでそうすること可能ですが、少し特殊なコードが必要です)。関連する質問への回答を参照してください複数のオブジェクトをpickleファイルに保存してロードしますか?これを行うさまざまな方法の詳細については。個人的には、@ Lutz Precheltの答えが一番好きです。これは、ここの例に適応したものです。

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

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

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

1
オブジェクトを保存する簡単な方法があると想像したので、これは私にはまれです... 'saveobject(company1、c:\ mypythonobjects)のようなもの
Peterstone

4
@Peterstone:1つのオブジェクトのみを格納したい場合は、私の例の約半分のコードしか必要ありません-意図的に、複数のオブジェクトを保存できる方法を示すために書いた方法で書きました(後で読み返す) from)同じファイル。
martineau

1
@Peterstone、責任の分離には非常に正当な理由があります。この方法では、酸洗いプロセスからのデータの使用方法に制限はありません。ディスクに保存することも、ネットワーク接続経由​​で送信することもできます。
Harald Scheirich 2010

3
@martinaeau、これは、オブジェクトをディスクに保存するための関数が1つだけである必要があるというperstonesの発言への応答でした。ピクルの責任は、オブジェクトをチャンクとして処理できるデータに変えることだけです。ファイルへの書き込みは、ファイルオブジェクトの責任です。物事を1つにまとめることで、再利用性を高めることができます。たとえば、漬けたデータをネットワーク接続を介して送信したり、データベースに保存したりできるため、すべての責任は実際のデータとは別に行われます<->オブジェクト変換
Harald Scheirich

1
とを削除company1company2ます。削除Companyして何が起こるかを見せてみませんか?
マイクマッカーンズ2015

49

オブジェクトがであると想定することは、かなり強力な仮定だと思いますclass。ない場合はどうなりclassますか?オブジェクトがインタープリターで定義されていないという前提もあります。インタプリタで定義されている場合はどうなりますか?また、属性が動的に追加された場合はどうなりますか?一部のpythonオブジェクト__dict__に作成後の属性が追加されている場合pickle、それらの属性の追加は考慮されません(つまりpickle、オブジェクト定義への参照によってシリアル化されるため、追加されたことが「忘れられます」。

すべてのこれらのケースでは、picklecPickle恐ろしくあなたを失敗する可能性があります。

object(任意に作成された)属性を保存する場合(オブジェクト定義に追加された後、またはその後に)…を使用するのが最善です。dillこれは、Pythonのほとんどすべてをシリアル化できるを使用することです。

クラスから始めます…

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> with open('company.pkl', 'wb') as f:
...     pickle.dump(company1, f, pickle.HIGHEST_PROTOCOL)
... 
>>> 

シャットダウンして再起動します...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> with open('company.pkl', 'rb') as f:
...     company1 = pickle.load(f)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1378, in load
    return Unpickler(file).load()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1090, in load_global
    klass = self.find_class(module, name)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1126, in find_class
    klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'Company'
>>> 

エラーpickleが発生しました。やってみようdilllambda適切な測定のために、別のオブジェクトタイプ(a )を投入します。

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill       
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> with open('company_dill.pkl', 'wb') as f:
...     dill.dump(company1, f)
...     dill.dump(company2, f)
... 
>>> 

そして今、ファイルを読みます。

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('company_dill.pkl', 'rb') as f:
...     company1 = dill.load(f)
...     company2 = dill.load(f)
... 
>>> company1 
<__main__.Company instance at 0x107909128>
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>>    

できます。pickle失敗する理由と失敗dillしない理由は、(ほとんどの場合)モジュールのようにdill扱わ__main__れ、参照によるピクル化の代わりに(そうするようにpickle)クラス定義をピクルすることができるからです。その理由dillpickle化することができますがlambda、それはそれに名前を与えることを...そして、酸洗魔法が発生する可能性があります。

実際に、作成したオブジェクトがたくさんある場合は特に、これらすべてのオブジェクトを保存する簡単な方法があります。Pythonセッション全体をダンプし、後で戻ってくるだけです。

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> dill.dump_session('dill.pkl')
>>> 

コンピューターをシャットダウンし、エスプレッソなどを楽しんでから、後で戻ってきます...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('dill.pkl')
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>> company2
<function <lambda> at 0x1065f2938>

唯一の大きな欠点は、それdillがpython標準ライブラリの一部ではないことです。したがって、サーバーにPythonパッケージをインストールできない場合は、使用できません。

ただし、システムにPythonパッケージをインストールできる場合は、を使用して最新バージョンdillを入手できますgit+https://github.com/uqfoundation/dill.git@master#egg=dill。そして、あなたはで最新のリリースされたバージョンを得ることができますpip install dill


私は取得していますTypeError: __new__() takes at least 2 arguments (1 given)使用しようとするとdill、オーディオファイルが含まれてかなり複雑なオブジェクトで(有望に見えました)。
MikeiLL 2014

1
@MikeiLL:TypeError正確には何をしているのですか?これは通常、クラスインスタンスをインスタンス化するときに、引数の数が間違っていることを示しています。これが上記の質問のワークフローの一部ではない場合、別の質問として投稿したり、メールで私に送信したり、dillgithubページに問題として追加したりできますか?
マイクマッカーンズ2014

3
フォローしている人のために、ここに@MikeLLが投稿した関連する質問があります-回答から、それは明らかにdill問題ではありませんでした。
martineau 2015

dilでも私はくれMemoryErrorます!そうcPicklepickleそしてhickle
ファリドAlijani

4

あなたはanycacheを使ってあなたのために仕事をすることができます。それはすべての詳細を考慮します:

  • バックエンドとしてディルを使用し、Python pickleモジュールを拡張して、Pythonのlambdaすべての優れた機能を処理します。
  • 異なるオブジェクトを異なるファイルに保存し、適切に再ロードします。
  • キャッシュサイズを制限する
  • キャッシュをクリアできます
  • 複数の実行間でオブジェクトを共有できます
  • 結果に影響を与える入力ファイルを尊重できます

myfuncインスタンスを作成する関数があると仮定します。

from anycache import anycache

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

@anycache(cachedir='/path/to/your/cache')    
def myfunc(name, value)
    return Company(name, value)

Anycacheはmyfunc最初に呼び出し、cachedir(関数名とその引数に応じて)一意の識別子をファイル名として使用して、結果をファイルにピクルします。連続して実行すると、ピクルされたオブジェクトがロードされます。cachedirがPythonの実行間で保持される場合、ピクルされたオブジェクトは以前のPythonの実行から取得されます。

詳細については、ドキュメントを参照してください


anycacheたとえば、classまたはのようなコンテナの複数のインスタンスを保存するためにどのように使用しますかlist(これは関数を呼び出した結果ではありませんでした)。
martineau

2

company1あなたの質問からpython3 を使った簡単な例。

import pickle

# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))

ただし、この回答で述べたように、ピクルスは失敗することがよくあります。したがって、実際に使用する必要がありますdill

import dill

# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.