¿Python pickle lambda funciona?

He leído en varios hilos que Python pickle / cPickle no puede cPickle funciones lambda. Sin embargo, el siguiente código funciona, usando Python 2.7.6:

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

¿Entonces qué está pasando? O, más bien, ¿cuál es el límite de las lambdas de decapado?

[EDITAR] Creo que sé por qué se ejecuta este código. Olvidé (¡lo siento!) Estoy ejecutando Python sin stack, que tiene una forma de micro-hilos llamados tasklets ejecutando una función. Estas tareas pueden detenerse, decaparse, desecharse y continuarse, así que supongo (preguntado en la lista de correo sin stack) que también proporciona una forma de encoger cuerpos de funciones.

Sí, Python puede encaminar las funciones lambda … pero solo si tienes algo que use copy_reg para registrar cómo copy_reg funciones lambda: el paquete dill carga el copy_reg que necesitas en el registro de pickle para ti, cuando 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  at 0x10aebdaa0> 

Obtenga dill aquí: https://github.com/uqfoundation

No, Python no puede encoger las funciones lambda:

 >>> import cPickle as pickle >>> s = pickle.dumps(lambda x,y: x+y) Traceback (most recent call last): File "", line 1, in  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 

No estoy seguro de lo que hiciste que tuvo éxito …

Python puede pickles lambdas. Cubriremos Python 2 y 3 por separado ya que la implementación de pickle es diferente en diferentes versiones de Python.

  • Python 2.7

pickle usa un registro de pickle que no es más que una asignación de type a la función que se utiliza para serializar (decapado) objetos de ese tipo. Puedes ver el registro de salmuera como:

 >> pickle.Pickler.dispatch {bool: , instance: , classobj: , float: , function: , int: , list: , long: , dict: , builtin_function_or_method: , NoneType: , str: , tuple: , type: , unicode: } 

Para seleccionar tipos personalizados, Python proporciona copy_reg módulo copy_reg para registrar nuestras funciones. Puedes leer más sobre esto aquí . De forma predeterminada, el módulo copy_reg admite el decapado de los siguientes tipos adicionales:

 >> import copy_reg >> copy_reg.dispatch_table {code: , complex: , _sre.SRE_Pattern: , posix.statvfs_result: , posix.stat_result: } 

Ahora, el tipo de funciones lambda es types.FunctionType . Sin embargo, la función incorporada para este tipo de function: no puede serializar las funciones lambda. Por lo tanto, todas las bibliotecas de terceros como dill , cloudpickle , etc. anulan el método incorporado para serializar las funciones lambda con alguna lógica adicional. Importemos dill y veamos lo que hace.

 >> import dill >> pickle.Pickler.dispatch {_pyio.BufferedReader: , _pyio.TextIOWrapper: , _pyio.BufferedWriter: , _pyio.BufferedRandom: , functools.partial: , operator.attrgetter: , operator.itemgetter: , cStringIO.StringI: , cStringIO.StringO: , bool: , cell: , instancemethod: , instance: , classobj: , code: , property: , method-wrapper: , dictproxy: , wrapper_descriptor: , getset_descriptor: , member_descriptor: , method_descriptor: , file: , float: , staticmethod: , classmethod: , function: , int: , list: , long: , dict: , builtin_function_or_method: , module: , NotImplementedType: , NoneType: , xrange: , slice: , ellipsis: , str: , tuple: , super: , type: , weakcallableproxy: , weakproxy: , weakref: , unicode: , thread.lock: } 

Ahora, vamos a tratar de encurtir la función lambda.

 >> pickle.loads(pickle.dumps(lambda x:x)) > 

¡¡Funciona!!

En Python 2 tenemos dos versiones de pickle

 import pickle # pure Python version pickle.__file__ # /python-2.7/lib64/python2.7/pickle.py import cPickle # C extension cPickle.__file__ # /python-2.7/lib64/python2.7/lib-dynload/cPickle.so 

Ahora, vamos a tratar de encurtir lambda con C implementación cPickle .

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

¿Qué salió mal? Veamos la tabla de despacho de cPickle .

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

La implementación de pickle y cPickle es diferente. Importing eneldo hace que solo la versión Python de pickle funcione. La desventaja de usar pickle lugar de cPickle es que puede ser hasta 1000 veces más lento que cPickle.

  • Python 3.6

En Python 3, no hay un módulo llamado cPickle . En cambio, tenemos pickle que tampoco es compatible con el pickling de las funciones lambda por defecto. Veamos su tabla de despacho:

 >> import pickle >> pickle.Pickler.dispatch_table  

Espere. Intenté buscar dispatch_table de pickle no _pickle . _pickle es la implementación alternativa y más rápida de C pickle. ¡Pero aún no lo hemos importado! Esta implementación de C se importa automáticamente, si está disponible, al final del módulo de pickle Python puro.

 # 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 

Todavía nos queda la cuestión de encurtir lambdas en Python 3. La respuesta es que NO puedes con el pickle nativo o _pickle . Deberá importar dill o cloudpickle y usar eso en lugar del módulo de pickle nativo.

 >> import dill >> dill.loads(dill.dumps(lambda x:x)) > 

Espero que esto aclare todas las dudas.