作为这个问题的后续行动: Is there an easy way to pickle a python function (or otherwise serialize its code)?
我想从上面的帖子中看到这个子弹的一个例子:
“如果函数引用了你需要读取的全局变量(包括导入的模块,其他函数等),你也需要对它们进行序列化,或者在远程端重新创建它们。我的例子只是给它远程进程的全局命名空间。“
我有一个简单的测试,我正在使用marshal:
将函数字节代码写入文件def g(self,blah):
print blah
def f(self):
for i in range(1,5):
print 'some function f'
g('some string used by g')
data = marshal.dumps(f.func_code)
file = open('/tmp/f2.txt', 'w')
file.write(data)
然后我开始一个新的python实例:
file = open('/tmp/f2.txt', 'r')
code = marshal.loads(file.read())
func2 = types.FunctionType(code, globals(), "some_func_name");
func2('blah')
这导致:
NameError: global name 'g' is not defined
这与我制作的包括g的不同方法无关。我尝试了基本相同的方法来发送g作为f但f仍然看不到g。如何进入全局命名空间,以便f在接收过程中可以使用它?
有人还建议将pyro作为如何执行此操作的示例。我已经尝试了解迪斯科项目中的相关代码。我拿了他们的dPickle类并尝试在独立应用程序中重新创建他们的disco / tests / test_pickle.py功能但没有成功。我的实验在使用转储调用执行函数编组时遇到了问题。无论如何,下一步可能是火探探险。
总之,我所遵循的基本功能是能够通过网络发送方法,并将所有基本的“工作空间”方法与其一起发送(如g)。
来自答案的更改示例:
工作函数编写:
import marshal, types
def g(blah):
print blah
def f():
for i in range(1,5):
print 'some function f'
g('blah string used by g')
f_data = marshal.dumps(f.func_code)
g_data = marshal.dumps(g.func_code);
f_file = open('/tmp/f.txt', 'w')
f_file.write(f_data)
g_file = open('/tmp/g.txt', 'w')
g_file.write(g_data)
使用function_reader:
import marshal, types
f_file = open('/tmp/f.txt', 'r')
g_file = open('/tmp/g.txt', 'r')
f_code = marshal.loads(f_file.read())
g_code = marshal.loads(g_file.read())
f = types.FunctionType(f_code, globals(), 'f');
g = types.FunctionType(g_code, globals(), 'g');
f()
答案 0 :(得分:18)
云包执行此操作 - 只需“pip install cloud”然后:
import cloud, pickle
def foo(x):
return x*3
def bar(z):
return foo(z)+1
x = cloud.serialization.cloudpickle.dumps(bar)
del foo
del bar
f = pickle.loads(x)
print f(3) # displays "10"
换句话说,只需调用cloudpickle.dump()或cloudpickle.dumps()就像使用pickle。*一样,然后使用原生的pickle.load()或pickle.loads()来解冻。
Picloud在LGPL下发布了'cloud'python包,其他开源项目已经在使用它(google为“cloudpickle.py”看一些)。 picloud.com上的文档让您了解这段代码的强大之处,以及为什么他们有动力将精力投入到通用代码酸洗工作中 - 他们的整个业务都围绕着它构建。这个想法是,如果你有cpu_intensive_function()并想在亚马逊的EC2网格上运行它,你只需要替换:
cpu_intensive_function(some, args)
with:
cloud.call(cpu_intensive_function, some, args)
后者使用cloudpickle来挑选任何相关代码和数据,将其发送到EC2,运行它,并在调用cloud.result()时将结果返回给您。 (Picloud以毫秒为单位计算,它很便宜,而且我一直用它来进行蒙特卡罗模拟和金融时间序列分析,当我需要数百个CPU核心时,每个只需几秒钟。我不能说足够好关于它的事情,我甚至不在那里工作。)
答案 1 :(得分:4)
我尝试过基本相同的方法将g发送为f但f仍然看不到g。如何进入全局命名空间,以便f在接收过程中可以使用它?
将其分配给全局名称g
。 (我看到你将f
分配给func2
而不是f
。如果您使用g
执行类似的操作,那么很明显为什么f
找不到g
。请记住,名称解析会在运行时发生 - 在您致电g
之前,系统不会查找f
。)
当然,我猜是因为你没有显示你用来做这个的代码。
最好创建一个单独的字典,用于您要解开的函数的全局命名空间 - 沙箱。这样他们所有的全局变量都将与你正在执行此操作的模块分开。所以你可能会这样做:
sandbox = {}
with open("functions.pickle", "rb") as funcfile:
while True:
try:
code = marshal.load(funcfile)
except EOFError:
break
sandbox[code.co_name] = types.FunctionType(code, sandbox, code.co_name)
在这个例子中,我假设您将所有函数中的代码对象一个接一个地放在一个文件中,当读取它们时,我得到代码对象的名称并将其用作两者的基础。函数对象的名称以及存储在沙箱字典中的名称。
在未修改的函数中,沙箱字典是他们的globals()
,因此在f()
内,g
从sandbox["g"]
获取其值。拨打f
即可:sandbox["f"]("blah")
答案 2 :(得分:3)
通过导入__main__
并使用该模块中可用的方法,您可以更好地处理全局对象。这是dill为了序列化python中的几乎任何东西所做的事情。基本上,当dill序列化交互式定义的函数时,它会在序列化和反序列化方面使用__main__
上的一些名称修改,使__main__
成为有效的模块。
>>> import dill
>>>
>>> def bar(x):
... return foo(x) + x
...
>>> def foo(x):
... return x**2
...
>>> bar(3)
12
>>>
>>> _bar = dill.loads(dill.dumps(bar))
>>> _bar(3)
12
实际上,dill将它的类型注册到pickle
注册表中,所以如果你有一些使用pickle
的黑盒代码并且你无法真正编辑它,那么只需导入dill就可以神奇地制作它没有monkeypatching第三方代码的工作。
或者,如果您希望将整个解释器会话作为“python图像”发送,dill也可以这样做。
>>> # continuing from above
>>> dill.dump_session('foobar.pkl')
>>>
>>> ^D
dude@sakurai>$ python
Python 2.7.5 (default, Sep 30 2013, 20:15:49)
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('foobar.pkl')
>>> _bar(3)
12
您可以轻松地将图像通过ssh发送到另一台计算机,并从那里开始,只要有pickle的版本兼容性以及有关python更改和正在安装的内容的常见警告。
答案 3 :(得分:2)
每个模块都有自己的全局变量,没有通用的全局变量。我们可以将恢复的功能“植入”到某个模块中,并像普通模块一样使用它。
- 保存 -
import marshal
def f(x):
return x + 1
def g(x):
return f(x) ** 2
funcfile = open("functions.pickle", "wb")
marshal.dump(f.func_code, funcfile)
marshal.dump(g.func_code, funcfile)
funcfile.close()
- 恢复 -
import marshal
import types
open('sandbox.py', 'w').write('') # create an empty module 'sandbox'
import sandbox
with open("functions.pickle", "rb") as funcfile:
while True:
try:
code = marshal.load(funcfile)
except EOFError:
break
func = types.FunctionType(code, sandbox.__dict__, code.co_name)
setattr(sandbox, code.co_name, func) # or sandbox.f = ... if the name is fixed
assert sandbox.g(3) == 16 # f(3) ** 2
# it is possible import them from other modules
from sandbox import g
:
你也可以导入一些模块.e.g。来自外部的“sys”到“sandbox”命名空间:
sandbox.sys = __import__('sys')
或相同:
exec 'import sys' in sandbox.__dict__
assert 'sys' in sandbox, 'Verify imported into sandbox'
您的原始代码可以正常工作如果您不是在ipython交互式中,而是在python程序或普通的python交互式中进行操作
Ipython使用了一些奇怪的命名空间,它不是sys.modules中任何模块的 dict 。普通python或任何主程序使用sys.modules['__main__'].__dict__
作为globals()。任何模块都使用that_module.__dict__
,这也没关系,只有ipython交互是一个问题。
答案 4 :(得分:0)
当被腌制的功能与酸洗一起进入主模块时,Dill(以及其他泡菜变体,云雀等)似乎起作用。如果您从另一个模块中挑选一个函数,那么当发生unpickling时,该模块名称必须存在。我似乎找不到解决这个限制的方法。