繁体   English   中英

Python:泡菜 class object 具有函数/可调用对象作为属性

[英]Python: Pickle class object that has functions/callables as attributes

我有自定义 class 继承自 functools.partial

from functools import partial
from typing import Callable

class CustomPartial(partial):
    def __new__(cls, func_name: str, func: Callable, *args, **kwargs):
        self=super(CustomPartial, cls).__new__(cls, func, *args, **kwargs)
        self.func_name = func_name
        return self

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

这段代码对于串行处理来说很好,即我可以根据需要创建这个 class 的对象并将它们作为普通函数调用。

我遇到的问题是,当我尝试使用这些 CustomPartial 对象之一作为 function 输入以进行joblib并行处理时。 基于抛出的异常

TypeError: CustomPartial.__new__() missing 1 required positional argument: 'func'

我总结说,当尝试在进程之间“反序列化”时,问题正在发生。

下面的代码是该问题的最小工作示例。 我尝试使用dill进行序列化并尝试实现__setstate__ / __getstate__函数,但似乎没有任何改变被抛出的异常。

import dill
from typing import Callable
from functools  import partial

class CustomPartial(partial):
    def __new__(cls, func_name: str, func: Callable, *args, **kwargs):
        self=super(CustomPartial, cls).__new__(cls, func, *args, **kwargs)
        self.func_name = func_name
        return self

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

add = lambda x, y: x+y

add_ten = partial(add, y=10)
custom_partial = CustomPartial('add_ten', add_ten)

print(dill.loads(dill.dumps(add_ten)))
# functools.partial(<function <lambda> at 0x7f7647eefa30>, y=10)

try:
    print(dill.loads(dill.dumps(custom_partial)))
except Error as err:
    print(err)
    # CustomPartial.__new__() missing 1 required positional argument: 'func'

任何解决此问题的帮助/方向将不胜感激:)

编辑:解决方案很复杂,因为partial使用__setstate__()

没有测试它,但您可能需要覆盖CustomPartial class 中的方法partial.__reduce__()以匹配其__new__()签名和额外的参数。

这是 Python 3.10 中的partial.__reduce__()定义:

def __reduce__(self):
    return type(self), (self.func,), (self.func, self.args,
           self.keywords or None, self.__dict__ or None)

您应该在返回的元组的第二项中包含额外的参数/属性,当解开此 class 的 object 时,它作为*args传递给__new__() 另外,由于partial使用__setstate__()来设置其__dict__属性,您需要注意这一点,否则func_name属性将被删除。 如果您至少使用 Python 3.8,并且如果您想保留原始的__setstate__()方法,则可以使用 reduce 值的第六个字段来传递控制如何进行更新的可调用对象。

尝试将此添加到您的 class 中:

def __reduce__(self):
    return (
        type(self),
        (self.func_name, self.func),
        (self.func, self.args, self.keywords or None, self.__dict__ or None),
        None,
        None,
        self._setstate
    )

@staticmethod
def _setstate(obj, state):
    func_name = obj.func_name
    obj.__setstate__(state)  # erases func_name
    obj.func_name = func_name

参考:https://docs.python.org/3/library/pickle.html#object.__reduce__

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM