Есть ли простой способ замариновать функцию python (или иным образом сериализовать ее код)?


Я пытаюсь передать функцию через сетевое соединение (через asyncore). Есть ли простой способ сериализовать функцию python (которая, по крайней мере, в этом случае не будет иметь побочных эффектов) для такой передачи?

в идеале я хотел бы иметь пару функций, подобных этим:

def transmit(func):
    obj = pickle.dumps(func)
    [send obj across the network]

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()
8 85

8 ответов:

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

import marshal
def foo(x): return x*x
code_string = marshal.dumps(foo.func_code)

затем в удаленном процессе (после передачи code_string):

import marshal, types

code = marshal.loads(code_string)
func = types.FunctionType(code, globals(), "some_func_name")

func(10)  # gives 100

несколько предостережений:

  • формат Маршала (любой байт-код python, если на то пошло) может быть несовместимым между основными python версии.

  • будет работать только для реализации cpython.

  • Если функция ссылается на глобалы (включая импортированные модули, другие функции и т. д.), которые вам нужно забрать, вам также нужно сериализовать их или воссоздать их на удаленной стороне. Мой пример просто дает ему глобальное пространство имен удаленного процесса.

  • вам, вероятно, нужно будет сделать немного больше, чтобы поддерживать более сложные случаи, такие как закрытие или функции генератора.

проверить укроп, который расширяет библиотеку pickle Python для поддержки большего разнообразия типов, включая функции:

>>> import dill as pickle
>>> def f(x): return x + 1
...
>>> g = pickle.dumps(f)
>>> f(1)
2
>>> pickle.loads(g)(1)
2

Он также поддерживает ссылки на объекты в закрытии функции:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3

самый простой способ, вероятно,inspect.getsource(object) (см. модуль проверить), которая возвращает строку с исходным кодом для функции или метода.

все зависит от того, генерируете ли вы функцию во время выполнения или нет:

если вы это сделаете - inspect.getsource(object) не будет работать для динамически генерируемых функций, поскольку он получает источник объекта от .py файл, поэтому в качестве источника могут быть получены только функции, определенные до выполнения.

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

единственное решение для динамического создания функции, которые я могу придумать, это построить функцию как строку перед передачей, передать источник, а затем eval() это на стороне приемника.

Edit: the marshal решение выглядит также довольно умным, не знал, что вы можете сериализовать что-то другое thatn встроенные

на cloud пакет (pip install cloud) может мариновать произвольный код, включая зависимости. См.https://stackoverflow.com/a/16891169/1264797.

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

y_serial.py модуль:: склад Python объектов с SQLite

"Serialization + persistance:: в нескольких строках кода сжимайте и аннотируйте объекты Python в SQLite; затем позже извлекайте их хронологически по ключевым словам без какого-либо SQL. Самый полезный" стандартный " модуль для базы данных для хранения без схемы данные."

http://yserial.sourceforge.net

code_string = '''
def foo(x):
    return x * 2
def bar(x):
    return x ** 2
'''

obj = pickle.dumps(code_string)

теперь

exec(pickle.loads(obj))

foo(1)
> 2
bar(3)
> 9