web-dev-qa-db-ja.com

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

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

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

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

191
Peterstone

標準ライブラリのpicklename__モジュールを使うことができます。これがあなたの例への基本的な応用です:

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')

更新:

これはとても一般的な回答なので、少し高度な使用法のトピックについて触れておきます。

cPicklename__(または_pickle)とpicklename__

ほとんどの場合、cPicklename__よりも実際に pickleNAME _ モジュールを使用することをお勧めします。前者はC言語で書かれており、はるかに速いからです。微妙な違いがいくつかありますが、ほとんどの場合、それらは同等であり、Cバージョンは非常に優れたパフォーマンスを提供します。それに切り替えるのは簡単ではありませんでした。単にimportname__ステートメントを次のように変更してください。

import cPickle as pickle

Python 3では、cPicklename__は_pickleと改名されましたが、picklename__モジュールが自動的に行うようになったため、これを行う必要はなくなりました - python 3のpickleと_pickleの違いは?

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

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

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

picklename__は、 documentation に記述されているように、 protocols と呼ばれる、いくつかの異なるPython固有のフォーマットでファイルを読み書きできます。 "Protocol version 0"は ASCII そしてそれゆえに「人間が読める」バージョン> 1はバイナリで、利用可能な最高バージョンは使用されているPythonのバージョンによって異なります。デフォルトはPythonのバージョンによっても異なります。 Python 2ではデフォルトはプロトコルバージョン0でしたが、Python 3.7ではプロトコルバージョン3です。 Python 3.xでは、モジュールにpickle.DEFAULT_PROTOCOLが追加されていましたが、それはPython 2には存在しません。

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

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

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

pickle.dump(obj, output, -1)

どちらの方法でも、複数のpickle操作で使用するためにPicklername__オブジェクトを作成した場合は、プロトコルを1回だけ指定する必要があります。

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

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

複数のオブジェクト

上のサンプルに示すように、pickleファイル can には任意の数のpickleオブジェクトを含めることができますが、未知数のオブジェクトがある場合は、それらをすべて可変サイズのコンテナに格納する方が簡単です。 listname__、Tuplename__、またはdictname__を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)

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

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))
381
martineau

オブジェクトがclassであると仮定するのはかなり強い仮定だと思います。 classではない場合はどうなりますか?オブジェクトがインタプリタで定義されていないという仮定もあります。インタプリタで定義されているとどうなりますか?また、属性が動的に追加された場合はどうなりますか?いくつかのpythonオブジェクトが作成後にそれらの__dict__に追加された属性を持っているとき、pickleはそれらの属性の追加を尊重しません(すなわち、pickleはオブジェクト定義への参照によって直列化されるので忘れられます)。

これらすべての場合において、pickleおよびcPickleはあなたを恐ろしく失敗させる可能性があります。

object(任意に作成されたもの)を保存しようと思っているのなら、(オブジェクト定義に追加された、あるいはその後に)属性を持っています…dillを使うのが最善の策です。

私たちはクラスから始めます…

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はそれを処理できません。 dillを試してみましょう。良い方法として、別のオブジェクト型(lambda)を投入します。

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が行うように)参照によるピッキングの代わりにクラス定義をpickle化できるためです。 dilllambdaを漬けることができるのは、それが名前をつけるからです…そして、酸洗いの魔法が起こり得るのです。

実際、たくさんのオブジェクトを作成した場合は特に、これらすべてのオブジェクトを簡単に保存する方法があります。 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パッケージをインストールできる場合は、git+https://github.com/uqfoundation/dill.git@master#Egg=dillを使用して最新のdillを取得できます。そして、あなたはpip install dillで最新リリース版を手に入れることができます。

43
Mike McKerns

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

  • これはバックエンドとして dill を使用します。これはpythonのpickleモジュールを拡張してlambdaとすべてのNice pythonの機能を処理します。
  • さまざまなオブジェクトをさまざまなファイルに格納し、それらを適切に再ロードします。
  • キャッシュサイズを制限する
  • キャッシュクリアを許可
  • 複数の実行間でオブジェクトを共有できます
  • 結果に影響を与える入力ファイルの尊重を許可します

インスタンスを作成する関数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の実行間で保存されている場合、pickleオブジェクトは前回のpythonの実行から取得されます。

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

3
c0fec0de