Есть ли простой способ выбрать функцию Python (или иным образом сериализовать ее код)? - PullRequest
90 голосов
/ 10 августа 2009

Я пытаюсь передать функцию через сетевое соединение (используя 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 ]

113 голосов
/ 10 августа 2009

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

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.

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

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

36 голосов
/ 06 декабря 2013

Извлеките 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
14 голосов
/ 10 августа 2009
10 голосов
/ 10 августа 2009

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

6 голосов
/ 10 августа 2009

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

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

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

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

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

5 голосов
/ 03 июня 2013

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

1 голос
/ 19 апреля 2018
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
1 голос
/ 13 сентября 2009

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

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

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

http://yserial.sourceforge.net

...