web-dev-qa-db-ja.com

dillを使用してクラス定義をシリアル化する方法は?

Python pickle:更新されたクラス定義の処理 への回答で、dillパッケージの作成者は次のように書いています。

「わかりました。この機能をgithubの最新リビジョンでディルに追加しました。思ったよりもはるかに少ないトリックで実装されました...クラス定義をpickleでシリアル化するだけです。」

dillをインストールし、それをいじくり回したので、dillでこの機能を実際に使用する方法は私にはわかりません。誰かが明確な例を提供できますか?クラスインスタンスをpickle化して、クラス定義をシリアル化したいです。

(私はpythonに不慣れであり、この機能は非常に重要であるように思われます。オブジェクトをピクルスにするときに、オブジェクトを見ることができるという保証にできるだけ近づくことが素晴らしいからです(シミュレーションの結果である可能性があります)将来、クラス定義が変更された後、簡単にアクセスできる方法ですべての変更を追跡していません。)

14
user8765

次の機能のいずれかをお探しだと思います…

ここでは、クラスとインスタンスを作成してから、クラス定義を変更します。 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 dill
>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x+self.y       
...   y = 1
... 
>>> f = Foo()
>>> _Foo = dill.dumps(Foo)
>>> _f = dill.dumps(f)
>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x*self.z  
...   z = -1 
... 
>>> f_ = dill.loads(_f, ignore=True)
>>> f_.y
1
>>> f_.bar(1)
2
>>> Foo_ = dill.loads(_Foo)
>>> g = Foo_()
>>> g.bar(1)
2

ピクルスは上で爆破するでしょう。 dillがクラスを明示的にシリアル化し、pickleが行うことを実行したくない場合は、dill.dumps(Foo, byref=True)を参照してdillにpickle化するように依頼できます。 。または、ignore=False(デフォルト)を使用して、新しく定義されたクラスを無視することを動的に決定できます。

ここで、以下のケースでは、新しいクラス定義を使用して、オブジェクトからソースを抽出し、それをファイルに保存します。さらに、ソースをファイル(ここでは一時ファイルを使用)にダンプして、後でインポートできるようにすることができます。

>>> sFoo = dill.source.getsource(Foo)
>>> print sFoo
class Foo(object):
  def bar(self, x):
    return x*self.z
  z = -1

>>> open('myFoo.py', 'w').write(sFoo)    
>>>
>>> f = dill.temp.dump_source(Foo, dir='.')
>>> f.name
'/Users/mmckerns/dev/tmpM1dzYN.py'
>>> from tmpM1dzYN import Foo as _Foo_
>>> h = _Foo_()
>>> h.bar(2)
-2
>>> from myFoo import Foo as _SFoo_
>>> _SFoo_.z
>>> -1
>>> 

それがお役に立てば幸いです。

10
Mike McKerns

この機能がそれほど重要であるとすれば、それは今では言語コアにあるでしょう。 :-)いいえ、これは高度な形式でPythonを使用することは重要ではありません-そして、古いモデルに基づいてオブジェクトを再インスタンス化できることに依存するプロジェクトがある場合-可能であれば、慎重に検討する必要があります。おそらく、古いモデルをシリアル化するのではなく、明示的なコードで保持する必要があります。

私のアドバイスは、実際に必要だと思うまで「それをそのままにして」、強力なモデル移行ポリシーなどの他のソリューションと比較することです。

そうは言っても、私はdillを試しましたが、宣伝どおりに機能します。pickleが「dump」および「dumps」呼び出しを使用して通常のオブジェクトで実行できるのと同じようにクラスをシリアル化し、「load」および「」でクラスオブジェクトを再構築できます。負荷」。

おそらく混乱しているのは、オブジェクトのシリアル化(pickleまたはdillを使用)には、そのソースコード(つまり、クラスを定義するために使用されるテキストの実際の行Pythonコード))も含まれていないことです。その名前も。

したがって、クラスの名前が「A」の場合、シリアル化されたときに、「アンディリング」後にその名前が必要になった場合は、グローバル名前空間でその名前をクラスに再割り当てする必要があります。元の名前は__name__属性に保持されます。 (そして、同じモデルの複数のバージョンが一緒に住むという目的のために、それは多くの衝突につながるでしょう)。

したがって:

class A(object):
    ...

import dill

dill.dump(A, open("myfile", "w"))

del A
....
someclass = dill.load(open("myfile"))
print (someclass.__name__)
globals()[someclass.__name__] = someclass
# at this point you have the "A" class back in the global namespace
6
jsbueno