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

发布于 2024-08-01 20:31:11 字数 313 浏览 6 评论 0原文

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

I'm trying to transfer a function across a network connection (using asyncore). Is there an easy way to serialize a python function (one that, in this case at least, will have no side effects) for transfer like this?

I would ideally like to have a pair of functions similar to these:

def transmit(func):
    obj = pickle.dumps(func)
    [send obj across the network]

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

夏了南城 2024-08-08 20:31:12

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

The cloud package (pip install cloud) can pickle arbitrary code, including dependencies. See https://stackoverflow.com/a/16891169/1264797.

忘年祭陌 2024-08-08 20:31:12
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
code_string = '''
def foo(x):
    return x * 2
def bar(x):
    return x ** 2
'''

obj = pickle.dumps(code_string)

Now

exec(pickle.loads(obj))

foo(1)
> 2
bar(3)
> 9
若有似无的小暗淡 2024-08-08 20:31:12

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

cloudpickle 对于使用 Python 的集群计算特别有用
代码可能通过网络传送以在远程主机上执行
接近数据。

使用示例:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)

Cloudpickle is probably what you are looking for.
Cloudpickle is described as follows:

cloudpickle is especially useful for cluster computing where Python
code is shipped over the network to execute on remote hosts, possibly
close to the data.

Usage example:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)
静若繁花 2024-08-08 20:31:12

您可以这样做:

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

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

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

You can do this:

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

Now, transmit(fn_generator()) will send the actual definiton of fn(x,y) instead of a reference to the module name.

You can use the same trick to send classes across network.

握住你手 2024-08-08 20:31:12

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

y_serial.py 模块 :: 用 SQLite 仓库 Python 对象

“序列化 + 持久化 :: 用几行代码,将 Python 对象压缩并注释到 SQLite 中;然后按时间顺序检索它们,无需任何 SQL。大多数数据库存储无模式数据的有用“标准”模块。”

http://yserial.sourceforge.net

The basic functions used for this module covers your query, plus you get the best compression over the wire; see the instructive source code:

y_serial.py module :: warehouse Python objects with SQLite

"Serialization + persistance :: in a few lines of code, compress and annotate Python objects into SQLite; then later retrieve them chronologically by keywords without any SQL. Most useful "standard" module for a database to store schema-less data."

http://yserial.sourceforge.net

贵在坚持 2024-08-08 20:31:12

这是一个帮助器类,您可以使用它来包装函数以使它们可挑选。 已经提到的关于 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)

Here is a helper class you can use to wrap functions in order to make them picklable. Caveats already mentioned for marshal will apply but an effort is made to use pickle whenever possible. No effort is made to preserve globals or closures across serialization.

    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)
自由如风 2024-08-08 20:31:11

您可以序列化函数字节码,然后在调用者上重建它。 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 实现。

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

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

You could serialise the function bytecode and then reconstruct it on the caller. The marshal module can be used to serialise code objects, which can then be reassembled into a function. ie:

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

Then in the remote process (after transferring code_string):

import marshal, types

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

func(10)  # gives 100

A few caveats:

  • marshal's format (any python bytecode for that matter) may not be compatable between major python versions.

  • Will only work for cpython implementation.

  • If the function references globals (including imported modules, other functions etc) that you need to pick up, you'll need to serialise these too, or recreate them on the remote side. My example just gives it the remote process's global namespace.

  • You'll probably need to do a bit more to support more complex cases, like closures or generator functions.

枫以 2024-08-08 20:31:11

查看 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

Check out Dill, which extends Python's pickle library to support a greater variety of types, including functions:

>>> import dill as pickle
>>> def f(x): return x + 1
...
>>> g = pickle.dumps(f)
>>> f(1)
2
>>> pickle.loads(g)(1)
2

It also supports references to objects in the function's closure:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3
无声静候 2024-08-08 20:31:11

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

The most simple way is probably inspect.getsource(object) (see the inspect module) which returns a String with the source code for a function or a method.

So尛奶瓶 2024-08-08 20:31:11

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

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

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

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

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

It all depends on whether you generate the function at runtime or not:

If you do - inspect.getsource(object) won't work for dynamically generated functions as it gets object's source from .py file, so only functions defined before execution can be retrieved as source.

And if your functions are placed in files anyway, why not give receiver access to them and only pass around module and function names.

The only solution for dynamically created functions that I can think of is to construct function as a string before transmission, transmit source, and then eval() it on the receiver side.

Edit: the marshal solution looks also pretty smart, didn't know you can serialize something other thatn built-ins

枯寂 2024-08-08 20:31:11

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

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

您可以pickle它,

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

您也可以pickle闭包,

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:未找到 __ main __.mypartial

使用 Python 2.7 和 3.6 进行测试

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文