Подтвердить что ты не робот

Сериализация и десериализация лямбда

Я хотел бы сериализовать на машине A и десериализовать на машине B питтон лямбда. Есть несколько очевидных проблем с этим:

  • модуль рассола не сериализует или десериализует код. Он только сериализует имена классов/методов/функций
  • некоторые из ответов, которые я нашел в google, предлагают использовать модуль маршала низкого уровня для сериализации атрибута func_code для лямбда, но они не могут описать, как можно восстановить объект функции из десериализованного объекта кода.
  • marhshal (l.func_code) не будет сериализовать замыкание, связанное с лямбдой, что приведет к проблеме обнаружения, когда данной лямбде действительно нужно закрытие и предупреждение пользователю о том, что он пытается сериализовать лямбда, которая использует замыкание

Следовательно, мой вопрос (ы):

  • как можно восстановить функцию из десериализованного (демаршаллированного) объекта кода?
  • как можно обнаружить, что данная лямбда не будет работать должным образом без соответствующего закрытия?
4b9b3361

Ответ 1

Удивительно, что проверка того, будет ли лямбда работать без связанного с ней закрытия, на самом деле довольно проста. Согласно документации по модели данных, вы можете просто проверить атрибут func_closure:

>>> def get_lambdas():
...     bar = 42
...     return (lambda: 1, lambda: bar)
...
>>> no_vars, vars = get_lambdas()
>>> print no_vars.func_closure
None
>>> print vars.func_closure
(<cell at 0x1020d3d70: int object at 0x7fc150413708>,)
>>> print vars.func_closure[0].cell_contents
42
>>>

Затем сериализация + загрузка лямбда довольно прямолинейна:

>>> import marshal, types
>>> old = lambda: 42
>>> old_code_serialized = marshal.dumps(old.func_code)
>>> new_code = marshal.loads(old_code_serialized)
>>> new = types.FunctionType(new_code, globals())
>>> new()
42

Стоит взглянуть на документацию для FunctionType:

function(code, globals[, name[, argdefs[, closure]]])

Create a function object from a code object and a dictionary.
The optional name string overrides the name from the code object.
The optional argdefs tuple specifies the default argument values.
The optional closure tuple supplies the bindings for free variables.

Обратите внимание, что вы также можете предоставить закрытие... Это означает, что вы даже можете сериализовать старое закрытие функции, а затем загрузить его на другом конце:)

Ответ 2

Я не уверен точно, что вы хотите сделать, но вы можете попробовать dill. Укроп может сериализовать и десериализовать лямбда, и я считаю, что он также работает для закрытия ягненка. API pickle API является подмножеством этого API. Чтобы использовать его, просто "импортируйте укроп, как рассол", и займитесь своими делами травления.

>>> import dill
>>> testme = lambda x: lambda y:x
>>> _testme = dill.loads(dill.dumps(testme))
>>> testme
<function <lambda> at 0x1d92530>
>>> _testme
<function <lambda> at 0x1d924f0>
>>> 
>>> def complicated(a,b):
...   def nested(x):
...     return testme(x)(a) * b
...   return nested
... 
>>> _complicated = dill.loads(dill.dumps(complicated))
>>> complicated 
<function complicated at 0x1d925b0>
>>> _complicated
<function complicated at 0x1d92570>

Dill регистрирует его типы в реестре pickle, поэтому, если у вас есть код черного ящика, который использует pickle, и вы не можете его редактировать, то просто импортировать укроп можно волшебным образом заставить его работать без monkeypatching стороннего участника код. Или, если вы хотите, чтобы весь сеанс интерпретатора передавался по проводу как "изображение на питоне", укроп также может это сделать.

>>> # continuing from above
>>> dill.dump_session('foobar.pkl')
>>>
>>> ^D
[email protected]>$ python
Python 2.7.5 (default, Sep 30 2013, 20:15:49) 
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('foobar.pkl')
>>> testme(4)
<function <lambda> at 0x1d924b0>
>>> testme(4)(5)
4
>>> dill.source.getsource(testme)
'testme = lambda x: lambda y:x\n'

Вы можете легко отправить изображение через ssh на другой компьютер и начать с того места, где вы остановились, до тех пор, пока совместимость версий марихуаны с версией и обычные оговорки об изменении python и устанавливаемых вещах. Как показано, вы также можете извлечь источник лямбда, который был определен в предыдущем сеансе.

У Dill также есть несколько хороших инструментов, помогающих понять, что заставляет ваш травление терпеть неудачу, когда ваш код не работает.