Травление статического метода в Python

Я пытаюсь рассортировать объект, содержащий ссылки на статические методы класса. Pickle терпит неудачу (например, на 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 (объект):       @picklable_staticmethod       def foo():           print "done."

Мои попытки в этом не увенчались успехом, особенно потому, что я не смог извлечь класс владельца из функции foo. Я даже хотел согласиться на явные спецификации (например, @picklable_staticmethod(MyClass)), но я не знаю, как обращаться к классу MyClass, где он определен.

Любые идеи были бы замечательными!

Ионатан

Ответы

Ответ 1

Кажется, что это работает.

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). Травление, рассыпание и вызов метода могут быть немного быстрее.

Ответ 2

EDIT: изменен после комментария Джейсона.

Я думаю, что python прав, не позволяя травить объект staticmethod - так как невозможно раскрыть методы экземпляра или класса! Такой объект будет иметь очень мало смысла вне его контекста:

Отметьте это: Учебник по описанию

import pickle

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

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

o = MyClass()

pickled = pickle.dumps(dosomething)

Это работает и что нужно делать - определить функцию, рассолить ее и использовать такую ​​функцию, как статический метод в определенном классе.

Если у вас есть прецедент для ваших нужд, напишите его, и я буду рад обсудить его.