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尝试腌制B它是一个A实例但在 2.7 上调用时,从未在 python 2.6 上调用注册函数。

这是一个已知的错误吗?

4

2 回答 2

3

Python 2.7.3 添加了一个新功能,允许对动态创建的类进行腌制。请参阅问题 7689

目前,即使用户通过 copy_reg 请求不同的 pickle 机制,也无法使用通常的 pickle 机制来 pickle 动态创建的类。附加的补丁通过简单地转置两个代码块使这种定制成为可能。

copy_reg 模块用于处理对象的自定义酸洗,这正是这里需要的。但是,#494904中检查元类实例的代码*就在*之前*它查看 copy_reg 调度表。这些补丁只是在 pickle.py 和 cPickle.c 中重新排序这些测试,以便可以为元类的实例注册一个自定义pickler。

2.7.3 更新日志

问题 #7689:当动态创建的类使用 copy_reg 注册时,允许对动态创建的类进行酸洗。Nicolas M. Thiéry 和 Craig Citro 的补丁。

对于 Python 2.6,解决此问题的唯一方法是将pickle.py模块移植到该版本。您可以尝试将 2.7.3pickle.py模块与 2.6 捆绑在一起,作为纯 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 回答