是否有一种简单的方法来挑选python函数(或以其他方式序列化其代码)?

Mic*_*ley 90 python function pickle

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

Bri*_*ian 114

您可以序列化函数字节码,然后在调用者上重建它.所述编组模块可用于序列化代码对象,然后可将其重新组装成一个函数.即:

import marshal
def foo(x): return x*x
code_string = marshal.dumps(foo.func_code)
Run Code Online (Sandbox Code Playgroud)

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

import marshal, types

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

func(10)  # gives 100
Run Code Online (Sandbox Code Playgroud)

一些警告:

  • marshal的格式(任何python字节码)在主要python版本之间可能无法兼容.

  • 只适用于cpython实现.

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

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

  • 谢谢。这正是我正在寻找的。基于一些粗略的测试,它可以像生成器一样工作。 (2认同)
  • 如果您阅读关于编组模块的前几段,您会看到强烈建议使用泡菜吗?对于pickle页面也是如此.http://docs.python.org/2/library/marshal.html (2认同)
  • 在Python 3.5.3上,`foo.func_code`引发`AttributeError`。还有另一种获取功能代码的方法吗? (2认同)

Jos*_*sen 36

查看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
Run Code Online (Sandbox Code Playgroud)

它还支持对函数闭包中对象的引用:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3
Run Code Online (Sandbox Code Playgroud)

  • dill还可以很好地从函数和lambdas中获取源代码,并将这些源代码保存到磁盘中,如果你更喜欢使用对象酸洗. (2认同)
  • 就可以了。而且也是一个嵌入式解决方案,它在导入后立即工作,无需修改 pickle 周围的任何其他代码。 (2认同)

Ric*_*dle 14

Pyro能够为你做到这一点.

  • 但这并不意味着你不能*看看Pyro的代码,看看它是如何完成的:) (18认同)
  • @ AaronDigulla-是的,但值得一提的是,在阅读其他人发布的代码之前,您应该始终检查软件的许可证.在许多情况下,阅读其他人的代码并重复使用这些想法而不引用来源或遵守许可/复制约束可能被视为抄袭和/或侵犯版权. (3认同)

Aar*_*lla 10

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


kur*_*zak 6

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

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

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

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

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


am7*_*m70 6

在现代 Python 中,您可以 pickle 函数和许多变体。考虑一下这个

import pickle, time
def foobar(a,b):
    print("%r %r"%(a,b))
Run Code Online (Sandbox Code Playgroud)

你可以腌制它

p = pickle.dumps(foobar)
q = pickle.loads(p)
q(2,3)
Run Code Online (Sandbox Code Playgroud)

你可以腌制封闭物

import functools
foobar_closed = functools.partial(foobar,'locked')
p = pickle.dumps(foobar_closed)
q = pickle.loads(p)
q(2)
Run Code Online (Sandbox Code Playgroud)

即使闭包使用局部变量

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

但如果使用内部函数关闭它,它将失败

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

有错误

pickle.PicklingError:无法 pickle <function mypartial at 0x7f3b6c885a50>:未找到 __ main __.mypartial

使用 Python 2.7 和 3.6 进行测试

  • 请注意,酸洗实际上并不序列化所有代码。这只是序列化对该函数的引用。这意味着只有当该函数将来存在时您才能运行它。考虑一下您想要在某个时间点重播代码的情况(不是您愿意的),但这不允许这样做 - 如果该函数不存在,您无法存储 pickled 函数并在移动的代码库中调用它不再以相同的形式存在或不存在。 (5认同)

ste*_*egt 5

cloud封装(PIP安装云)可以泡制任意代码,包括依赖关系.请参阅/sf/answers/1182381861/.