4

次のコードが与えられます:

import pickle
import copy_reg


class A(type):
    pass


class B(object):
    __metaclass__ = A


def _reduce_a(a):
    print('Hey there')
    return a.__name__


copy_reg.pickle(A, _reduce_a)

pickle.dumps(B)

登録された関数は、インスタンスである_reduce_aピクルスを試行するときにpython 2.6で呼び出されることはありませんが、2.7で呼び出されます。BA

これは既知のバグですか?

4

2 に答える 2

3

Python 2.7.3 に新しい機能が追加され、動的に作成されたクラスを pickle 化できるようになりました。問題 7689を参照してください。

現在、ユーザーが copy_reg を介して別の pickle メカニズムを要求したとしても、通常の pickle メカニズムを使用して動的に作成されたクラスを pickle することはできません。添付のパッチを使用すると、2 つのコード ブロックを入れ替えるだけで、このカスタマイズが可能になります。

copy_reg モジュールは、オブジェクトのカスタム ピクルを処理するために存在します。これは、まさにここで必要なものです。ただし、メタクラスのインスタンスをチェックする#494904のコードは、copy_reg ディスパッチ テーブルを調べる*直前に*これを行います。パッチは pickle.py と cPickle.c でこれらのテストの順序を変更するだけで、メタクラスのインスタンスにカスタム ピックラーを登録できます。

そして2.7.3 変更ログから:

問題 #7689: メタクラスが copy_reg に登録されている場合、動的に作成されたクラスのピクルを許可します。Nicolas M. Thiéry と Craig Citro によるパッチ。

Python 2.6 でこれを回避する唯一の方法は、pickle.pyモジュールをそのバージョンに移植することです。2.7.3 モジュールを 2.6 にバンドルすることもできpickle.pyます。これは、純粋な Python 実装として動作するはずです。

pickle.Pickler.saveまたは、メソッドにモンキー パッチを適用します。

from copy_reg import dispatch_table
from types import TypeType, StringType, TupleType
from pickle import Pickler, PicklingError


def pickler_save(self, obj):
    # Check for persistent id (defined by a subclass)
    pid = self.persistent_id(obj)
    if pid:
        self.save_pers(pid)
        return

    # Check the memo
    x = self.memo.get(id(obj))
    if x:
        self.write(self.get(x[0]))
        return

    # Check the type dispatch table
    t = type(obj)
    f = self.dispatch.get(t)
    if f:
        f(self, obj) # Call unbound method with explicit self
        return

    # Check copy_reg.dispatch_table
    reduce = dispatch_table.get(t)
    if reduce:
        rv = reduce(obj)
    else:
        # Check for a class with a custom metaclass; treat as regular class
        try:
            issc = issubclass(t, TypeType)
        except TypeError: # t is not a class (old Boost; see SF #502085)
            issc = 0
        if issc:
            self.save_global(obj)
            return

        # Check for a __reduce_ex__ method, fall back to __reduce__
        reduce = getattr(obj, "__reduce_ex__", None)
        if reduce:
            rv = reduce(self.proto)
        else:
            reduce = getattr(obj, "__reduce__", None)
            if reduce:
                rv = reduce()
            else:
                raise PicklingError("Can't pickle %r object: %r" %
                                    (t.__name__, obj))

    # Check for string returned by reduce(), meaning "save as global"
    if type(rv) is StringType:
        self.save_global(obj, rv)
        return

    # Assert that reduce() returned a tuple
    if type(rv) is not TupleType:
        raise PicklingError("%s must return string or tuple" % reduce)

    # Assert that it returned an appropriately sized tuple
    l = len(rv)
    if not (2 <= l <= 5):
        raise PicklingError("Tuple returned by %s must have "
                            "two to five elements" % reduce)

    # Save the reduce() output and finally memoize the object
    self.save_reduce(obj=obj, *rv)


Pickler.save = pickler_save

上記のモンキー パッチを使用すると、例は Python 2.6.8 で動作します。

>>> pickle.dumps(B)
Hey there
'c__main__\nB\np0\n.'
于 2013-04-04T08:21:48.713 に答える
0

2.6 のバグ/制限だと思います。を呼び出すと関数が呼び出されることがわかりますが、呼び出すと呼び出されcopy.copy(B)ませんpickle.dumps(B)

于 2013-03-19T05:47:47.213 に答える