У меня есть класс (Bar), который фактически имеет собственное состояние и обратные вызовы и используется другим классом (Foo):

 class Foo(object):

     def __init__(self):
         self._bar = Bar(self.say, 10)
         self._bar.work()

     def say(self, msg):
         print msg

 class Bar(object):
     def __init__(self, callback, value):
         self._callback = callback
         self._value = value
         self._more = { 'foo' : 1, 'bar': 3, 'baz': 'fubar'}

     def work(self):
         # Do some work
         self._more['foo'] = 5
         self._value = 10
         self._callback('FooBarBaz')

 Foo()

Очевидно, я не могу выделить класс Foo, поскольку Bar имеет метод экземпляра, поэтому у меня осталось следующее решение реализации __getstate__ & __setstate__ в Bar, чтобы сохранить self._value и self._more, но я также должен создать экземпляр метода self._callback (т.е. вызвать __init__() из внешнего класса Foo, передав функцию обратного вызова .

Но я не могу понять, как этого добиться.

Любая помощь приветствуется.
Спасибо.

2
nbaztec 6 Авг 2012 в 19:14

1 ответ

Лучший ответ

Я думаю, что если вам нужно сериализовать что-то подобное, вам нужно иметь возможность определить обратный вызов как строку. Например, вы можете сказать, что callback = 'myproject.callbacks.foo_callback'.

Обычно в __getstate__ вы должны заменить функцию _callback чем-то, что вы могли бы использовать для поиска функции позже, например self._callback.__name__.

В __setstate__ вы должны заменить _callback функцией.

Это зависит от того, имеют ли все ваши функции настоящие имена, поэтому вы не можете использовать лямбда в качестве обратного вызова и ожидать, что он будет сериализован. Вам также понадобится разумный механизм для поиска ваших функций по имени.

Вы можете потенциально использовать __import__ (что-то вроде: 'myproject.somemodule.somefunc' синтаксис имени с точками может поддерживаться таким образом, см. http://code.google.com/p/mock/source/browse/mock.py#1076 ) или просто определите таблицу поиска в ваш код.

Просто быстрый (непроверенный, извините!) Пример, предполагающий, что у вас есть небольшой набор возможных обратных вызовов, определенных в таблице поиска:

def a():
    pass

callbacks_to_name = {a: 'a'
                     # ...
                     }

callbacks_by_name = {'a': a,
             # ...
             }

class C:
    def __init__(self, cb):
        self._callback = cb

    def __getstate__(self):
        self._callback = callbacks_to_name[self._callback]
        return self.__dict__

    def __setstate__(self, state):
        state[_callback] = callbacks_by_name[self._callback]

Я не уверен, каков ваш вариант использования, но я бы рекомендовал сделать это, сериализовав ваши рабочие элементы в JSON или XML и написав простой набор функций для их сериализации и десериализации самостоятельно.

Преимущество состоит в том, что сериализованный формат может быть прочитан и понят людьми и изменен при обновлении программного обеспечения. Pickle заманчиво, потому что кажется достаточно близким, но к тому времени, когда у вас будет серьезная куча __getstate__ и __setstate__, вы действительно не сэкономите много усилий или головной боли, создавая собственную схему специально для вашего приложения. .

1
stderr 6 Авг 2012 в 19:56
Вы, сэр, только что дали мне новое направление. Функции не являются лямбдами, поэтому, безусловно, могут иметь полностью определенные имена. Теперь тестируем код ...
 – 
nbaztec
9 Авг 2012 в 17:44
Почти готово, просто нужно найти способ получить полное имя функции, а затем разбить его, чтобы использовать в getattr()
 – 
nbaztec
9 Авг 2012 в 17:58
Я не могу получить FQN для функции, как показано здесь ссылка. Он возвращает __main__.instancemethod.functionname вместо отображения имени класса. Какие-либо предложения?
 – 
nbaztec
9 Авг 2012 в 18:09
Боюсь, что до Python 3.3 и PEP-0395 (qualname) вы захотите убрать все из своего модуля __main__. python.org/dev/peps / pep-0395 / # в рассоле
 – 
stderr
9 Авг 2012 в 22:47
Спасибо @Mike, но все это время я возился с этим, и на самом деле мне удалось правильно обработать / распаковать метод, но он выглядит как unbound (и я не могу привязать к нему себя во время выполнения). Я использую собственные методы для сохранения данных и установки их другим методом после init()
 – 
nbaztec
10 Авг 2012 в 10:20