python - pickle Cython 修饰函数导致 PicklingError

标签 python cython decorator pickle dill

我有以下代码:

def decorator(func):

    @functools.wraps(func)
    def other_func():
        print('other func')

    return other_func

@decorator
def func():
    pass

如果我尝试 pickle func 一切正常。但是,如果我将模块编译为 Cython 扩展,它将失败。 这是错误:

>>>> pickle.dumps(module.func)

PicklingError: Can't pickle <cyfunction decorator.<locals>.other_func at 0x102a45a58>: attribute lookup other_func on module failed

如果我使用 dill 而不是 pickle,也会发生同样的情况。

你知道怎么解决吗?

最佳答案

我认为您在这里无能为力。它看起来像是 Cython 中可能存在的错误。但可能有一个很好的理由来解释为什么 Cython 会做我不知道的事情。

问题的出现是因为 Cython 函数在 Python 领域中作为内置函数公开(例如 mapall 等)。这些函数不能更改其名称属性。然而,Cython 试图使其函数更像纯 Python 函数,因此提供了修改其多个属性的能力。但是,Cython 函数还实现了 __reduce__,它自定义了对象如何被 pickle 序列化。看起来这个函数确实认为可以更改函数对象的名称,因此忽略这些值并使用正在包装的内部 PyCFunction 结构的名称(github blob)。

您能做的最好的事情就是提交错误报告。您可能能够创建一个薄包装器,而不是使您的函数被序列化,但这会在函数被调用时增加开销。

自定义 Pickle

您可以使用 PicklerUnpicklerpersistent_id 功能来覆盖 Cython 提供的自定义实现。以下是如何为特定类型/对象自定义 pickle 。它是使用纯 Python 函数完成的,但您可以轻松地对其进行更改以处理 Cython 函数。

import pickle
from importlib import import_module
from io import BytesIO

# example using pure python
class NoPickle:
    def __init__(self, name):
        # emulating a function set of attributes needed to pickle
        self.__module__ = __name__
        self.__qualname__ = name

    def __reduce__(self):
        # cannot pickle this object
        raise Exception


my_object = NoPickle('my_object')

# pickle.dumps(obj) # error!

# use persistent_id/load to help dump/load cython functions

class CustomPickler(pickle.Pickler):
    def persistent_id(self, obj):
        if isinstance(obj, NoPickle):
            # replace with NoPickle with type(module.func) to get the correct type
            # alternatively you might want to include a simple cython function 
            # in the same module to make it easier to get the write type.
            return "CythonFunc" , obj.__module__, obj.__qualname__
        else:
            # else return None to pickle the object as normal
            return None

class CustomUnpickler(pickle.Unpickler):
    def persistent_load(self, pid):
        if pid[0] == "CythonFunc":
            _, mod_name, func_name = pid
            return getattr(import_module(mod_name), func_name)
        else:
            raise pickle.UnpicklingError('unsupported pid')

bytes_ = BytesIO()
CustomPickler(bytes_).dump(my_object)

bytes_.seek(0)
obj = CustomUnpickler(bytes_).load()

assert obj is my_object

关于python - pickle Cython 修饰函数导致 PicklingError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53776530/

相关文章:

c# - 如何修复其根类需要装饰器实例的装饰器?

python - 如何在Python中将前景文本与嘈杂的背景分开?

python - 3 个 cython/python 函数调用之间的差异

python - 如何找到并单击带有 Selenium 的隐藏按钮?

python - Cython Memoryview 作为返回值

c++ - 调试从 C++ 调用的 Cython 代码中的崩溃

python - 为什么函数装饰器中的 **kwargs 值与函数中的值不同?

python - 了解在单个语句中使用多个 python 装饰器

python - 正则表达式 : How can I capture multiple text from this string?

python - 如何使用 jenkins 从私有(private) github 存储库安装 python 包?