在Python中将一个staticmethod进行Pickling

3

我一直在尝试pickle一个包含对静态类方法的引用的对象。Pickle失败了(例如在module.MyClass.foo上),并声明它无法被pickle,因为module.foo不存在。
我想出了以下解决方案,使用一个包装器对象来定位函数调用时的位置,保存容器类和函数名称:

class PicklableStaticMethod(object):
    """Picklable version of a static method.
    Typical usage:
        class MyClass:
            @staticmethod
            def doit():
                print "done"
        # This cannot be pickled:
        non_picklable = MyClass.doit
        # This can be pickled:
        picklable = PicklableStaticMethod(MyClass.doit, MyClass)
    """
    def __init__(self, func, parent_class):
        self.func_name = func.func_name
        self.parent_class = parent_class
    def __call__(self, *args, **kwargs):
        func = getattr(self.parent_class, self.func_name)
        return func(*args, **kwargs)

我在想,是否有更好的方法 - 更标准的方法 - 来pickle这样一个对象呢?我不想对全局pickle过程进行更改(例如使用copy_reg),但以下模式会很棒: class MyClass(object): @picklable_staticmethod def foo(): print "done."

我的尝试都没有成功,具体原因是我无法从foo函数中提取owner类。我甚至愿意接受明确的规定(例如@picklable_staticmethod(MyClass)),但我不知道任何一种方式可以在定义它的地方引用MyClass类。 任何想法都会很棒!
2个回答

5

这似乎是有效的。

class PickleableStaticMethod(object):
    def __init__(self, fn, cls=None):
        self.cls = cls
        self.fn = fn
    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)
    def __get__(self, obj, cls):
        return PickleableStaticMethod(self.fn, cls)
    def __getstate__(self):
        return (self.cls, self.fn.__name__)
    def __setstate__(self, state):
        self.cls, name = state
        self.fn = getattr(self.cls, name).fn

把握好在获取静态方法时抓住类的技巧。
替代方案:您可以使用元类将所有静态方法都赋予一个“.__parentclass__”属性。然后,您可以子类化Pickler并为每个子类实例提供自己的“.dispatch”表,这样您就可以修改它而不影响全局分发表(Pickler.dispatch)。然后进行pickling、unpickling和调用方法可能会更快一些。

如果原始类不可用,则此方法将无效,这(我认为)违背了对静态方法进行腌制和还原的目的 - 您允许它被腌制,但是需要整个类来将其还原。 - Alan Franzoni
尝试一下:import pickleclass MyClass(object): @PickleableStaticMethod def dosomething(a, b): print a, bpickled = pickle.dumps(MyClass.dosomething)del MyClassstm = pickle.loads(pickled) - Alan Franzoni
我觉得我可能刚才说的话完全错了,pickle在处理函数方面的工作方式与我预期的完全不同。忽略我最近的两个评论 :-/ - Alan Franzoni
嗨,杰森。你能否解释一下两件事情:1)为什么应该有_get_方法——我认为它可以避免;2)我认为在反序列化时,如果cls=None,它将无法工作。 - Ph0en1x

0

编辑:在Jason的评论后进行了修改。

我认为Python在不允许对staticmethod对象进行pickle处理时是正确的 - 因为无法pickle处理实例或类方法!这样的对象在其上下文之外几乎没有意义:

请查看:描述符教程

import pickle

def dosomething(a, b):
    print a, b

class MyClass(object):
    dosomething = staticmethod(dosomething) 

o = MyClass()

pickled = pickle.dumps(dosomething)

这个方法是有效的,也是应该采用的 - 定义一个函数,将其pickle化,并将此类函数作为staticmethod在某个类中使用。

如果您有需要的用例,请写下来,我很乐意讨论。


那么这个问题没有被正确表述,OP说“我一直在尝试pickle一个包含静态类方法引用的对象”,但他实际上是在尝试pickle一个静态方法,而不是一个包含对静态方法引用的对象。 - Alan Franzoni
让我用一个例子来澄清。 我有一个任务生成机制。例如: class MyClass(TaskGenerator): @staticmethod def certain_task(): print "task done" def generate_task(self): return FuncTask(MyClass.certain_task)我想要将生成的任务pickle,但目前无法实现。 我可以使用全局函数 - 直到现在为止我一直在使用 - 但我不喜欢为了pickle支持而将其从“正确的位置”取出。 - Yonatan
我承认我仍然不理解你在尝试做什么,而且我仍然认为你可能滥用了“静态方法”(你在何处、如何以及为什么反序列化这些对象?) - Alan Franzoni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接