我有以下代码:
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 领域中作为内置函数公开(例如 map
、all
等)。这些函数不能更改其名称属性。然而,Cython 试图使其函数更像纯 Python 函数,因此提供了修改其多个属性的能力。但是,Cython 函数还实现了 __reduce__
,它自定义了对象如何被 pickle
序列化。看起来这个函数确实认为可以更改函数对象的名称,因此忽略这些值并使用正在包装的内部 PyCFunction 结构的名称(github blob)。
您能做的最好的事情就是提交错误报告。您可能能够创建一个薄包装器,而不是使您的函数被序列化,但这会在函数被调用时增加开销。
自定义 Pickle
您可以使用 Pickler
和 Unpickler
的 persistent_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/