我有以下代码:
def decorator(func):
@functools.wraps(func)
def other_func():
print('other func')
return other_func
@decorator
def func():
pass如果我尝试使用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,也会发生同样的情况。
你知道怎么修吗?
发布于 2018-12-14 20:19:10
我不认为在这里你真的能做什么。它看起来像是Cython中的一个可能的bug。但是,也许有一个很好的理由来解释为什么Cython会做我不知道的事情。
出现这个问题是因为Cython函数在Python中被暴露为内置函数(例如,map、all等)。不能更改这些函数的名称属性。然而,Cython试图使它的函数更像纯Python函数,因此提供了修改它们的几个属性的能力。但是,Cython函数还实现了定制pickle序列化对象方式的__reduce__。看起来这个函数确实认为函数对象的名称是可以更改的,因此忽略这些值,并使用被包装的内部PyCFunction结构的名称(github blob)。
你能做的最好的事情就是提交一份bug报告。您也许能够创建一个薄包装器,使您的函数能够被序列化,但这将在调用函数时增加开销。
定制Pickle
您可以使用Pickler和Unpickler的persistent_id特性来覆盖Cython提供的自定义实现。下面是如何为特定类型/对象定制酸洗。这是用一个纯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_objecthttps://stackoverflow.com/questions/53776530
复制相似问题