web-dev-qa-db-fra.com

Can Python pickle lambda fonctions?

J'ai lu dans un certain nombre de threads que Python pickle/cPickle ne peut pas décaper les fonctions lambda. Cependant, le code suivant fonctionne, en utilisant Python 2.7.6:

import cPickle as pickle

if __== "__main__":
    s = pickle.dumps(lambda x, y: x+y)
    f = pickle.loads(s)
    assert f(3,4) == 7

Alors, quoi de neuf? Ou plutôt, quelle est la limite du décapage des lambdas?

[EDIT] Je pense que je sais pourquoi ce code fonctionne. J'ai oublié (désolé!) J'exécute du python sans pile, qui a une forme de micro-threads appelés tasklets exécutant une fonction. Ces tasklets peuvent être arrêtées, décapées, décapées et poursuivies, donc je suppose (demandé sur la liste de diffusion sans pile) qu'il fournit également un moyen de décaper les corps de fonction.

47
Lars

Oui, python peut décaper les fonctions lambda… mais seulement si vous avez quelque chose qui utilise copy_reg pour enregistrer comment décaper les fonctions lambda - le package dill charge le copy_reg vous avez besoin du registre des cornichons pour vous, lorsque vous import dill.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> import dill  # the code below will fail without this line
>>> 
>>> import pickle
>>> s = pickle.dumps(lambda x, y: x+y)
>>> f = pickle.loads(s)
>>> assert f(3,4) == 7
>>> f
<function <lambda> at 0x10aebdaa0>

obtenez l'aneth ici: https://github.com/uqfoundation

67
Mike McKerns

Non, Python ne peut pas décaper les fonctions lambda:

>>> import cPickle as pickle
>>> s = pickle.dumps(lambda x,y: x+y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py", line 70, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle function objects

Je ne sais pas ce que vous avez fait qui a réussi ...

31
Ned Batchelder

Python peut décaper les lambdas. Nous couvrirons Python 2 et 3 séparément car l'implémentation de pickle est différente dans les différentes versions Python.

  • Python 2.7

pickle utilise registre de cornichons qui n'est rien d'autre qu'un mappage de type vers la fonction à utiliser pour la sérialisation (pickling) objets de ce type. Vous pouvez voir registre de cornichons comme:

>> pickle.Pickler.dispatch

{bool: <function pickle.save_bool>,
 instance: <function pickle.save_inst>,
 classobj: <function pickle.save_global>,
 float: <function pickle.save_float>,
 function: <function pickle.save_global>,
 int: <function pickle.save_int>,
 list: <function pickle.save_list>,
 long: <function pickle.save_long>,
 dict: <function pickle.save_dict>,
 builtin_function_or_method: <function pickle.save_global>,
 NoneType: <function pickle.save_none>,
 str: <function pickle.save_string>,
 Tuple: <function pickle.save_Tuple>,
 type: <function pickle.save_global>,
 unicode: <function pickle.save_unicode>}

Pour décaper des types personnalisés, Python fournit le module copy_reg Pour enregistrer nos fonctions. Vous pouvez en savoir plus ici . Par défaut, le module copy_reg Prend en charge le décapage des types supplémentaires suivants:

>> import copy_reg
>> copy_reg.dispatch_table

{code: <function ipykernel.codeutil.reduce_code>,
 complex: <function copy_reg.pickle_complex>,
 _sre.SRE_Pattern: <function re._pickle>,
 posix.statvfs_result: <function os._pickle_statvfs_result>,
 posix.stat_result: <function os._pickle_stat_result>}

Maintenant, le type de fonctions lambda est types.FunctionType. Cependant, la fonction intégrée pour ce type function: <function pickle.save_global> N'est pas en mesure de sérialiser les fonctions lambda. Par conséquent, toutes les bibliothèques tierces comme dill, cloudpickle, etc. remplacent la méthode intégrée pour sérialiser les fonctions lambda avec une logique supplémentaire. Importons dill et voyons ce qu'il fait.

>> import dill
>> pickle.Pickler.dispatch

{_pyio.BufferedReader: <function dill.dill.save_file>,
 _pyio.TextIOWrapper: <function dill.dill.save_file>,
 _pyio.BufferedWriter: <function dill.dill.save_file>,
 _pyio.BufferedRandom: <function dill.dill.save_file>,
 functools.partial: <function dill.dill.save_functor>,
 operator.attrgetter: <function dill.dill.save_attrgetter>,
 operator.itemgetter: <function dill.dill.save_itemgetter>,
 cStringIO.StringI: <function dill.dill.save_stringi>,
 cStringIO.StringO: <function dill.dill.save_stringo>,
 bool: <function pickle.save_bool>,
 cell: <function dill.dill.save_cell>,
 instancemethod: <function dill.dill.save_instancemethod0>,
 instance: <function pickle.save_inst>,
 classobj: <function dill.dill.save_classobj>,
 code: <function dill.dill.save_code>,
 property: <function dill.dill.save_property>,
 method-wrapper: <function dill.dill.save_instancemethod>,
 dictproxy: <function dill.dill.save_dictproxy>,
 wrapper_descriptor: <function dill.dill.save_wrapper_descriptor>,
 getset_descriptor: <function dill.dill.save_wrapper_descriptor>,
 member_descriptor: <function dill.dill.save_wrapper_descriptor>,
 method_descriptor: <function dill.dill.save_wrapper_descriptor>,
 file: <function dill.dill.save_file>,
 float: <function pickle.save_float>,
 staticmethod: <function dill.dill.save_classmethod>,
 classmethod: <function dill.dill.save_classmethod>,
 function: <function dill.dill.save_function>,
 int: <function pickle.save_int>,
 list: <function pickle.save_list>,
 long: <function pickle.save_long>,
 dict: <function dill.dill.save_module_dict>,
 builtin_function_or_method: <function dill.dill.save_builtin_method>,
 module: <function dill.dill.save_module>,
 NotImplementedType: <function dill.dill.save_singleton>,
 NoneType: <function pickle.save_none>,
 xrange: <function dill.dill.save_singleton>,
 slice: <function dill.dill.save_slice>,
 Ellipsis: <function dill.dill.save_singleton>,
 str: <function pickle.save_string>,
 Tuple: <function pickle.save_Tuple>,
 super: <function dill.dill.save_functor>,
 type: <function dill.dill.save_type>,
 weakcallableproxy: <function dill.dill.save_weakproxy>,
 weakproxy: <function dill.dill.save_weakproxy>,
 weakref: <function dill.dill.save_weakref>,
 unicode: <function pickle.save_unicode>,
 thread.lock: <function dill.dill.save_lock>}

Maintenant, essayons de décaper la fonction lambda.

>> pickle.loads(pickle.dumps(lambda x:x))
<function __main__.<lambda>>

Ça marche!!

Dans Python 2, nous avons deux versions de pickle -

import pickle # pure Python version
pickle.__file__ # <install directory>/python-2.7/lib64/python2.7/pickle.py

import cPickle # C extension
cPickle.__file__ # <install directory>/python-2.7/lib64/python2.7/lib-dynload/cPickle.so

Maintenant, essayons de décaper lambda avec l'implémentation C cPickle.

>> import cPickle
>> cPickle.loads(cPickle.dumps(lambda x:x))
TypeError: can't pickle function objects

Qu'est ce qui ne s'est pas bien passé? Voyons la table de répartition de cPickle.

>> cPickle.Pickler.dispatch_table
AttributeError: 'builtin_function_or_method' object has no attribute 'dispatch_table'

L'implémentation de pickle et cPickle est différente. Importing aneth fait uniquement fonctionner la version Python de pickle. L'inconvénient d'utiliser pickle au lieu de cPickle est qu'il peut être 1000 fois plus lent que cPickle.

  • Python 3.6

Dans Python 3, il n'y a pas de module nommé cPickle. Nous avons à la place pickle qui ne prend pas en charge le décapage des fonctions lambda par défaut. Voyons voir sa table de répartition:

>> import pickle
>> pickle.Pickler.dispatch_table
<member 'dispatch_table' of '_pickle.Pickler' objects>

Attendez. J'ai essayé de rechercher dispatch_table de pickle pas _pickle. _pickle Est l'implémentation C alternative et plus rapide du cornichon. Mais nous ne l'avons pas encore importé! Cette implémentation C est importée automatiquement, si elle est disponible, à la fin du module pur Python pickle.

# Use the faster _pickle if possible
try:
    from _pickle import (
        PickleError,
        PicklingError,
        UnpicklingError,
        Pickler,
        Unpickler,
        dump,
        dumps,
        load,
        loads
    )
except ImportError:
    Pickler, Unpickler = _Pickler, _Unpickler
    dump, dumps, load, loads = _dump, _dumps, _load, _loads

Il nous reste la question du décapage des lambdas dans Python 3. La réponse est que vous NE POUVEZ PAS avec le pickle ou _pickle. Vous devrez importer dill ou cloudpickle et l'utiliser à la place du module de pickle natif.

>> import dill
>> dill.loads(dill.dumps(lambda x:x))
<function __main__.<lambda>>

J'espère que cela efface tous les doutes.

14
Saim Raza

Même si cela peut être évident, je voudrais ajouter une autre solution possible. Comme vous le savez probablement, les fonctions lambda ne sont que des déclarations de fonctions anonymes. Si vous n'avez pas beaucoup de lambdas qui ne sont utilisés qu'une seule fois et que cela n'ajoute pas beaucoup de bruit à votre code, vous pouvez simplement nommer votre lambda et lui passer le nom (sans les parenthèses) comme ceci:

import cPickle as pickle

def addition(x, y):
    return x+y


if __== "__main__":
    s = pickle.dumps(addition)
    f = pickle.loads(s)
    assert f(3,4) == 7

Le nom ajoute également plus de sémantique et vous n'auriez pas besoin d'une dépendance supplémentaire comme Dill. Mais ne faites cela que si cela l'emporte sur le bruit supplémentaire de la ou des fonctions supplémentaires.

0
Sandro

Installer l'aneth

$ pip install dill

Touchez un fichier

touch yeah.p

Maintenant, exécutez ce script python3,

import dill

dill.dump(lambda x:x+1, open('yeah.p', 'wb'))
my_lambda = dill.load(open('yeah.p', 'rb'))
print(my_lambda(2))  # 3
0
juan Isaza