126

ネットワーク接続を介して関数を転送しようとしています(非同期を使用)。このような転送のためにPython関数(この場合は少なくとも副作用がないもの)をシリアル化する簡単な方法はありますか?

理想的には、次のような2つの関数が必要です。

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

関数のバイトコードをシリアル化してから、呼び出し元で再構築できます。マーシャルモジュールを使用してコードオブジェクトをシリアル化し、それを関数に再アセンブルできます。すなわち:

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)inspectモジュールを参照)関数またはメソッドのソースコードを含む文字列を返すことです。

于 2009-08-10T07:29:42.833 に答える
7

それはすべて、実行時に関数を生成するかどうかによって異なります。

実行するinspect.getsource(object)と、動的に生成された関数はファイルからオブジェクトのソースを取得するため機能しません.py。そのため、実行前に定義された関数のみをソースとして取得できます。

そして、とにかく関数がファイルに配置されている場合は、レシーバーにそれらへのアクセスを許可し、モジュール名と関数名のみを渡すのはなぜですか。

私が考えることができる動的に作成された関数の唯一の解決策は、送信前に関数を文字列として構築し、ソースを送信し、次にeval()それを受信側で構築することです。

編集:marshalソリューションもかなりスマートに見えますが、他の組み込みのものをシリアル化できることを知りませんでした

于 2009-08-10T09:22:44.993 に答える
5

cloudパッケージ(pip install cloud)は、依存関係を含む任意のコードを実行できます。https://stackoverflow.com/a/16891169/1264797を参照してください。

于 2013-06-03T15:41:56.223 に答える
5

最新のPythonでは、関数や多くのバリエーションをピクルスにすることができます。このことを考慮

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:ピクルスできません<function mypartial at 0x7f3b6c885a50>:__main__。mypartialとして見つかりません

Python2.7および3.6でテスト済み

于 2020-10-28T18:24:08.843 に答える
3
code_string ='' '
def foo(x):
    x*2を返す
def bar(x):
    x**2を返します
'''

obj = pickle.dumps(code_string)

exec(pickle.loads(obj))

foo(1)
> 2
bar(3)
> 9
于 2018-04-19T14:30:37.183 に答える
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 に答える
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 に答える