有没有简单的方法来 pickle 一个 python 函数(或者以其他方式序列化它的代码)?
- 2025-02-27 09:07:00
- admin 原创
- 63
问题描述:
我正在尝试通过网络连接传输一个函数(使用 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.__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 实现。
如果函数引用了您需要获取的全局变量(包括导入的模块、其他函数等),您也需要序列化它们,或者在远程端重新创建它们。我的示例只是为其提供了远程进程的全局命名空间。
您可能需要做更多的事情来支持更复杂的情况,例如闭包或生成器函数。
解决方案 2:
查看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
解决方案 3:
最简单的方法可能是inspect.getsource(object)
(参见检查模块)它返回一个包含函数或方法源代码的字符串。
解决方案 4:
Pyro能够为您做到这一点。
解决方案 5:
这完全取决于您是否在运行时生成该函数:
如果这样做 -inspect.getsource(object)
将不适用于动态生成的函数,因为它从.py
文件中获取对象的源,因此只有在执行之前定义的函数才能作为源被检索。
如果您的函数无论如何都放在文件中,为什么不让接收者访问它们并只传递模块和函数名称。
我能想到的动态创建函数的唯一解决方案是在传输之前将函数构造为字符串,传输源,然后eval()
在接收端传输它。
编辑:这个marshal
解决方案看起来也很聪明,不知道你还能序列化其他内置的东西
解决方案 6:
在现代 Python 中,你可以 pickle 函数和许多变体。考虑一下
import pickle, time
def foobar(a,b):
print("%r %r"%(a,b))
你可以腌制它
p = pickle.dumps(foobar)
q = pickle.loads(p)
q(2,3)
你可以腌制封口
import functools
foobar_closed = functools.partial(foobar,'locked')
p = pickle.dumps(foobar_closed)
q = pickle.loads(p)
q(2)
即使闭包使用局部变量
def closer():
z = time.time()
return functools.partial(foobar,z)
p = pickle.dumps(closer())
q = pickle.loads(p)
q(2)
但如果你使用内部函数关闭它,它就会失败
def builder():
z = 'internal'
def mypartial(b):
return foobar(z,b)
return mypartial
p = pickle.dumps(builder())
q = pickle.loads(p)
q(2)
有错误
pickle.PicklingError: 无法 pickle <function mypartial at 0x7f3b6c885a50>: 未找到 __ main __.mypartial
已使用 Python 2.7 和 3.6 进行测试
解决方案 7:
该cloud
包 (pip install cloud) 可以 pickle 任意代码,包括依赖项。请参阅https://stackoverflow.com/a/16891169/1264797。
解决方案 8:
代码字符串 = '''
定义 foo(x):
返回 x * 2
定义条形图(x):
返回 x ** 2
'''
obj = pickle.dumps(代码字符串)
现在
执行(pickle.loads(obj))
foo(1)
> 2
酒吧(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)
解决方案 10:
您可以这样做:
def fn_generator():
def fn(x, y):
return x + y
return fn
现在,transmit(fn_generator())
将发送模块名称的实际定义fn(x,y)
而不是引用。
您可以使用相同的技巧通过网络发送课程。
解决方案 11:
该模块使用的基本功能涵盖了您的查询,此外您还可以通过网络获得最佳压缩;请参阅指导性源代码:
y_serial.py 模块:: 使用 SQLite 仓库 Python 对象
“序列化 + 持久性:: 用几行代码将 Python 对象压缩并注释到 SQLite 中;然后按关键字按时间顺序检索它们,而无需任何 SQL。用于数据库存储无模式数据的最有用的“标准”模块。”
http://yserial.sourceforge.net
解决方案 12:
这是一个辅助类,您可以使用它来包装函数,使它们可 picklable。前面提到的注意事项marshal
将适用,但我们会尽可能使用 pickle。不会在序列化过程中保留全局变量或闭包。
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)
扫码咨询,免费领取项目管理大礼包!