Травление статического метода в 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)
Это работает и что нужно делать - определить функцию, рассолить ее и использовать такую функцию, как статический метод в определенном классе.
Если у вас есть прецедент для ваших нужд, напишите его, и я буду рад обсудить его.