126

我正在尝试通过网络连接传输功能(使用 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()
4

12 回答 12

136

您可以序列化函数字节码,然后在调用者上重建它。marshal模块可用于序列化代码对象,然后可以将其重新组合成一个函数。IE:

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

然后在远程进程中(在传输code_string之后):

import marshal, types

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

func(10)  # gives 100

一些警告:

  • marshal 的格式(与此相关的任何 python 字节码)可能在主要的 python 版本之间不兼容。

  • 仅适用于 cpython 实现。

  • 如果函数引用了您需要获取的全局变量(包括导入的模块、其他函数等),您也需要将它们序列化,或者在远程端重新创建它们。我的示例只是为其提供了远程进程的全局命名空间。

  • 您可能需要做更多的工作来支持更复杂的情况,例如闭包或生成器函数。

于 2009-08-10T08:58:22.967 回答
47

查看Dill,它扩展了 Python 的 pickle 库以支持更多种类的类型,包括函数:

>>> 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
于 2013-12-06T06:20:22.313 回答
14

Pyro能够为您做到这一点

于 2009-08-10T07:45:33.723 回答
12

最简单的方法可能是inspect.getsource(object)(参见检查模块),它返回一个带有函数或方法源代码的字符串。

于 2009-08-10T07:29:42.833 回答
7

这完全取决于您是否在运行时生成函数:

如果你这样做 -inspect.getsource(object)将不适用于动态生成的函数,因为它从.py文件中获取对象的源,因此只有在执行之前定义的函数可以作为源检索。

如果你的函数无论如何都放在文件中,为什么不让接收者访问它们,只传递模块和函数名称。

我能想到的动态创建函数的唯一解决方案是在传输之前将函数构造为字符串,传输源,然后eval()在接收端。

编辑:这个marshal解决方案看起来也很聪明,不知道你可以序列化其他内置的东西

于 2009-08-10T09:22:44.993 回答
5

cloud(pip install cloud)可以pickle任意代码,包括依赖项。请参阅https://stackoverflow.com/a/16891169/1264797

于 2013-06-03T15:41:56.223 回答
5

In modern Python you can pickle functions, and many variants. Consider this

import pickle, time
def foobar(a,b):
    print("%r %r"%(a,b))

you can pickle it

p = pickle.dumps(foobar)
q = pickle.loads(p)
q(2,3)

you can pickle closures

import functools
foobar_closed = functools.partial(foobar,'locked')
p = pickle.dumps(foobar_closed)
q = pickle.loads(p)
q(2)

even if the closure uses a local variable

def closer():
    z = time.time()
    return functools.partial(foobar,z)
p = pickle.dumps(closer())
q = pickle.loads(p)
q(2)

but if you close it using an internal function, it will fail

def builder():
    z = 'internal'
    def mypartial(b):
        return foobar(z,b)
    return mypartial
p = pickle.dumps(builder())
q = pickle.loads(p)
q(2)

with error

pickle.PicklingError: Can't pickle <function mypartial at 0x7f3b6c885a50>: it's not found as __ main __.mypartial

Tested with Python 2.7 and 3.6

于 2020-10-28T18:24:08.843 回答
3

Cloudpickle可能是您正在寻找的。Cloudpickle 描述如下:

cloudpickle 对于集群计算特别有用,其中 Python 代码通过网络传输以在远程主机上执行,可能靠近数据。

使用示例:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)
于 2019-10-07T12:29:06.603 回答
3
code_string = '''
定义 foo(x):
    返回 x * 2
定义栏(x):
    返回 x ** 2
'''

obj = pickle.dumps(code_string)

现在

执行(pickle.loads(obj))

富(1)
> 2
酒吧(3)
> 9
于 2018-04-19T14:30:37.183 回答
2

你可以这样做:

def fn_generator():
    def fn(x, y):
        return x + y
    return fn

现在,transmit(fn_generator())将发送实际的定义fn(x,y)而不是对模块名称的引用。

您可以使用相同的技巧通过网络发送课程。

于 2019-11-18T18:47:53.677 回答
1

该模块使用的基本功能涵盖了您的查询,此外您还可以通过网络获得最佳压缩;请参阅指导性源代码:

y_serial.py 模块 :: 使用 SQLite 存储 Python 对象

“序列化 + 持久性 :: 在几行代码中,将 Python 对象压缩和注释为 SQLite;然后在没有任何 SQL 的情况下通过关键字按时间顺序检索它们。数据库存储无模式数据的最有用的“标准”模块。”

http://yserial.sourceforge.net

于 2009-09-13T05:35:44.707 回答
0

这是一个帮助类,您可以使用它来包装函数以使它们可腌制。已经提到的警告marshal将适用,但尽可能使用泡菜。没有努力在序列化过程中保留全局变量或闭包。

    class PicklableFunction:
        def __init__(self, fun):
            self._fun = fun

        def __call__(self, *args, **kwargs):
            return self._fun(*args, **kwargs)

        def __getstate__(self):
            try:
                return pickle.dumps(self._fun)
            except Exception:
                return marshal.dumps((self._fun.__code__, self._fun.__name__))

        def __setstate__(self, state):
            try:
                self._fun = pickle.loads(state)
            except Exception:
                code, name = marshal.loads(state)
                self._fun = types.FunctionType(code, {}, name)
于 2020-03-11T12:02:57.550 回答