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.
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
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 ...
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.
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.
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.
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.
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