首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >酸洗Cython修饰函数导致PicklingError

酸洗Cython修饰函数导致PicklingError
EN

Stack Overflow用户
提问于 2018-12-14 17:09:52
回答 1查看 570关注 0票数 3

我有以下代码:

代码语言:javascript
复制
def decorator(func):

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

    return other_func

@decorator
def func():
    pass

如果我尝试使用func,一切都会正常工作。但是,如果我将该模块编译为Cython扩展,它将失败。下面是错误:

代码语言:javascript
复制
>>>> pickle.dumps(module.func)

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

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

你知道怎么修吗?

EN

回答 1

Stack Overflow用户

发布于 2018-12-14 20:19:10

我不认为在这里你真的能做什么。它看起来像是Cython中的一个可能的bug。但是,也许有一个很好的理由来解释为什么Cython会做我不知道的事情。

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

你能做的最好的事情就是提交一份bug报告。您也许能够创建一个薄包装器,使您的函数能够被序列化,但这将在调用函数时增加开销。

定制Pickle

您可以使用PicklerUnpicklerpersistent_id特性来覆盖Cython提供的自定义实现。下面是如何为特定类型/对象定制酸洗。这是用一个纯python函数完成的,但您可以很容易地将其更改为处理Cython函数。

代码语言:javascript
复制
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
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53776530

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档