2016-05-03 2 views
5

pickle参照states thatピクルすることができるオブジェクトのセットは、かなり限定されている。確かに、私はdinamically-生成されたクラスを返す関数を持っている、と私はそのクラスのインスタンスを酸洗いすることはできませんが見つかりました:派生クラスを使用すると、私は "ローカルオブジェクトをpickle"できますか?

>>> import pickle 
>>> def f(): 
...  class A: pass 
...  return A 
... 
>>> LocalA = f() 
>>> la = LocalA() 
>>> with open('testing.pickle', 'wb') as f: 
...  pickle.dump(la, f, pickle.HIGHEST_PROTOCOL) 
... 
Traceback (most recent call last): 
    File "<stdin>", line 2, in <module> 
AttributeError: Can't pickle local object 'f.<locals>.A' 

ようなオブジェクトはpickleのためにあまりにも複雑です。 OK。さて、魔法は、似たようなオブジェクトをピクルしようとすると、派生したクラスのピクルを試みると動作します。

>>> class DerivedA(LocalA): pass 
... 
>>> da = DerivedA() 
>>> with open('testing.pickle', 'wb') as f: 
...  pickle.dump(da, f, pickle.HIGHEST_PROTOCOL) 
... 
>>> 

ここで何が起こっているのですか?これが簡単なのであれば、pickleは、この回避策を使用してdumpメソッドを実装して「ローカルオブジェクト」をピックすることができないのはなぜですか?

答えて

8

私はあなたが慎重にthe reference you citeを読んでいないと思います。 (DEF使用して、ない>ラムダ)モジュールのトップレベルで定義された

  • 機能トップレベルで定義された
  • 組み込み関数:参照も明確にのみ、次のオブジェクトがpickleableあることを述べてモジュールのモジュールのトップレベル

あなたの例で定義されている

  • クラス

    >>> def f(): 
    ...  class A: pass 
    ...  return A 
    

    モジュールのトップレベルでクラスが定義されていない、それはf()範囲内にクラスを定義しています。 pickleグローバルクラスで動作し、ローカルクラスでは動作しません。これにより、ピクル可能なテストが自動的に失敗します。

    DerivedAはグローバルクラスなので、すべて正常です。唯一のトップレベル(あなたにグローバル)のクラスや関数を漬けすることができない理由については

    、同様に(太字鉱山)質問参照の答え:関数は(ビルトイン

    注意ユーザー定義)は、値ではなく、の「完全修飾名」参照で節約されます。これは、関数が定義されているモジュールの名前と共に、関数名だけがpickleされることを意味します。ファンクションのコードもそのファンクション属性も、いずれもピクチャされません。したがって、定義モジュールはunpickle環境でインポート可能でなければならず、モジュールには名前付きオブジェクトが含まれていなければなりません。それ以外の場合は例外が発生します。

    同様に、クラスは名前付き参照で節約されるため、unpickle環境の同じ制限が適用されます。

    だからあなたはそれを持っています。 pickleは、オブジェクト内に含まれる生の命令ではなく、名前参照によってオブジェクトをシリアライズするだけです。これは、pickle'sジョブがオブジェクト階層とそれ以外のものをシリアル化するためです。

  • +0

    いいえ、クラスの名前だけがピクルされます。クラスそのものが(何らかの形で)保存されたと思っていましたが、これは基本クラスを節約することを意味しますが、これはpickleableではありません。 – fonini

    +0

    ところで、無関係なクラスがありますが同じ名前の環境で私はそれをアンピクルすると、この無関係なクラスのフランクシュタインのモンスターオブジェクトを得るでしょうが、古いクラスの属性を持っていますか?私はそれが沸騰すると思う:漬け物は私が思ったように自己完結型ではありません。 – fonini

    +1

    @foniniまた、リファレンスから: "...クラスインスタンスがピクルされると、クラスのコードとデータは一緒にピクルされません。インスタンスデータのみがピクルされます。"そう、正しい動作のように聞こえます。 :) –

    2

    DerivedAインスタンスは、DerivedAが完全修飾名と一致するグローバル変数で利用可能であるため、pickleableです。つまり、pickleはアンピクル時にクラスを検索します。

    ローカルクラスでこのようなことをしようとするときの問題は、Aクラスがインスタンスに対応するものが何もないことです。 fを2回実行すると、2つのAクラスが得られます。どちらのクラスが、プログラムの別の実行からのunpickled Aインスタンスのクラスであるかを判断する方法はありません。 fを一切実行しないと、Aクラスになります。次に、unpickleインスタンスのタイプについてどうしますか?

    3

    私はそれに同意しない、あなたは両方を漬けることができます。 dillのような優れたシリアライザを使用するだけです。 dill(デフォルト)は、参照によってピクルス化するのではなく、クラス定義を保存してクラスをピクルス化するので、最初のケースでは失敗しません。必要に応じて、dillを使用してソースコードを入手することもできます。

    >>> import dill as pickle 
    >>> def f(): 
    ... class A: pass 
    ... return A 
    ... 
    >>> localA = f() 
    >>> la = localA() 
    >>> 
    >>> _la = pickle.dumps(la) 
    >>> la_ = pickle.loads(_la) 
    >>>  
    >>> class DerivedA(localA): pass 
    ... 
    >>> da = DerivedA() 
    >>> _da = pickle.dumps(da) 
    >>> da_ = pickle.loads(_da) 
    >>> 
    >>> print(pickle.source.getsource(la_.__class__)) 
        class A: pass 
    
    >>>