Есть ли простой способ рассортировать функцию 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()

Ответ 1

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

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

Затем в удаленном процессе (после передачи кода_строки):

import marshal, types

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

func(10)  # gives 100

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

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

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

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

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

Ответ 2

Отъезд Dill, который расширяет библиотеку рассола 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

Ответ 4

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

Ответ 5

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

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

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

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

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

Ответ 6

Пакет cloud (облако установки пакетов) может различать произвольный код, включая зависимости. См. fooobar.com/questions/46237/....

Ответ 7

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

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

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

http://yserial.sourceforge.net

Ответ 8

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

Ответ 9

Cloudpickle, вероятно, то, что вы ищете. Cloudpickle описывается следующим образом:

cloudpickle особенно полезен для кластерных вычислений, где Python код передается по сети для выполнения на удаленных хостах, возможно близко к данным.

Пример использования:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)