Не думаю, что здесь можно что-то сделать.Похоже, возможная ошибка в Cython.Но может быть веская причина того, почему Cython делает то, что он делает, о чем я не знаю.
Проблема возникает из-за того, что функции Cython представляются как встроенные функции в земле Python (например, map
, all
и т. Д.).Эти функции не могут изменять свои атрибуты имени.Тем не менее, Cython пытается сделать свои функции более похожими на чистые функции Python, и поэтому предоставляет возможность изменять некоторые их атрибуты.Однако функции Cython также реализуют __reduce__
, который настраивает, как объекты сериализуются с помощью pickle
.Похоже, что эта функция действительно думает, что имя объекта функции можно изменить, и поэтому игнорирует эти значения и использует имя внутренней структуры PyCFunction, которая упаковывается ( github blob ).
Лучшее, что вы можете сделать, это отправить сообщение об ошибке.Возможно, вам удастся создать тонкую оболочку, которая позволит сериализовать вашу функцию, но это вызовет дополнительные издержки при вызове функции.
Настройка Pickle
Вы можете использовать функцию persistent_id
Pickler
и Unpickler
для переопределения пользовательской реализации, предоставленной 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_object