在Python中对静态方法进行序列化

3 投票
2 回答
4855 浏览
提问于 2025-04-15 17:05

我一直在尝试把一个包含静态类方法引用的对象进行序列化(也就是“腌制”)。但是在序列化的时候,比如说在module.MyClass.foo上,系统提示说无法进行序列化,因为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过程(比如使用copy_reg),但我希望能有这样的模式: class MyClass(object): @picklable_staticmethod def foo(): print "完成了。"

我尝试过这种方法,但都没有成功,特别是因为我无法从foo函数中提取出拥有者类。我甚至愿意接受显式指定(比如@picklable_staticmethod(MyClass)),但我不知道在定义它的地方怎么引用MyClass类。

如果有任何想法,那就太好了!

Yonatan

2 个回答

0

编辑:根据Jason的评论进行了修改。

我认为Python不允许对静态方法对象进行序列化是正确的,因为实例方法或类方法是无法被序列化的!这样的对象在它的上下文之外几乎没有意义:

看看这个:描述符教程

import pickle

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

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

o = MyClass()

pickled = pickle.dumps(dosomething)

这样做是可行的,这也是应该采取的做法——定义一个函数,序列化它,然后在某个类中将这个函数作为静态方法使用。

如果你有具体的需求,请写下来,我很乐意和你讨论。

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

诀窍在于,当你从类中获取静态方法时,抓住这个类。

还有其他方法:你可以使用元类(metaclass)来给所有的静态方法添加一个 .__parentclass__ 属性。这样你就可以创建一个 Pickler 的子类,并给每个子类的实例一个自己的 .dispatch 表,这样你就可以修改它,而不会影响全局的调度表(Pickler.dispatch)。这样进行序列化、反序列化和调用方法可能会稍微快一些。

撰写回答