14

例の使用

def foo(a):
    def bar(b):
        return a+b
    return bar

d = {1:foo(1), 2:foo(2)}

モジュール スコープで定義されていない関数では pickle モジュールが機能しないようです。考慮すべき別のピクルス化メカニズムはありますか?

4

3 に答える 3

22

残念ながら、ネストされた関数をピクルすることはできません。

モジュールはpickle関数を名前でシリアライズします。つまりmyfunc、モジュールmymoduleに関数がある場合、名前mymodule.myfuncを保存し、シリアル化を解除するときに再度検索します。(これはセキュリティと互換性に関する重要な問題です。これにより、シリアル化解除コードが、侵害または廃止される可能性のある元の定義ではなく、関数に対して独自の定義を使用することが保証されるためです。)

残念ながら、pickleネストされた関数ではそれを行うことはできません。名前でそれらを直接アドレス指定する方法がないためです。barたとえば、関数は の外部からアクセスできませんfoo

関数のように機能するシリアライズ可能なオブジェクトが必要な場合は、代わりに__call__メソッドを使用してクラスを作成できます。

class foo(object):
    def __init__(self, a):
        self.a = a
    def __call__(self, b): # the function formerly known as "bar"
        return self.a + b

これは、質問のネストされた関数と同じように機能し、問題はありませんpicklefooただし、インスタンスを非シリアル化するときは、同じクラス定義を使用できるようにする必要があることに注意してください。

于 2012-08-18T20:35:23.857 に答える
16

dillの代わりに使用すると、ネストされた関数をピクルできますpickle

>>> import dill
>>>    
>>> def foo(a):
...   def bar(b):
...     return a+b
...   return bar
... 
>>> d = {1:foo(1), 2:foo(2)}
>>> 
>>> _d = dill.dumps(d)
>>> d_ = dill.loads(_d)
>>> d_
{1: <function bar at 0x108cfe848>, 2: <function bar at 0x108cfe8c0>}
>>> d[1](0) + d[2](10)
13
>>> 
于 2016-02-24T13:28:25.533 に答える
0

Blckknght の回答によると。ネストされた関数が唯一の抽出シリアル化タイプであり、それを として使用する場合は、内部関数定義の先頭にdecorator追加functools.warpsして、他の解釈を導き、正しい名前を見つけることができます。

from functools import warps

def foo(func):
    @wraps(func)
    def bar(b):
        return func(b)
    return bar

@foo
def zzz(b):
    return b

于 2020-05-11T06:03:34.803 に答える