Есть ли простой способ рассортировать функцию 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
Ответ 3
Pyro может сделать это за вас.
Ответ 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)