Python, cPickle, травильные лямбда-функции

Мне нужно рассортировать массив таких объектов:

import cPickle as pickle
from numpy import sin, cos, array
tmp = lambda x: sin(x)+cos(x)
test = array([[tmp,tmp],[tmp,tmp]],dtype=object)
pickle.dump( test, open('test.lambda','w') )

и он дает следующую ошибку:

TypeError: can't pickle function objects

Есть ли способ обойти это?

Ответ 1

Встроенный модуль pickle не может сериализовать несколько видов объектов python (включая функции лямбда, вложенные функции и функции, определенные в командной строке).

Пакет picloud включает в себя более прочный сортировщик, который может раскрыть лямбда-функции.

from pickle import dumps
f = lambda x: x * 5
dumps(f) # error
from cloud.serialization.cloudpickle import dumps
dumps(f) # works

Сериализованные PiCloud объекты могут быть де-сериализованы с использованием функций normal pickle/cPickle load и loads.

Dill также обеспечивает аналогичную функциональность

>>> import dill           
>>> f = lambda x: x * 5
>>> dill.dumps(f)
'\x80\x02cdill.dill\n_create_function\nq\x00(cdill.dill\n_unmarshal\nq\x01Uec\x01\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00C\x00\x00\x00s\x08\x00\x00\x00|\x00\x00d\x01\x00\x14S(\x02\x00\x00\x00Ni\x05\x00\x00\x00(\x00\x00\x00\x00(\x01\x00\x00\x00t\x01\x00\x00\x00x(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x08\x00\x00\x00<lambda>\x01\x00\x00\x00s\x00\x00\x00\x00q\x02\x85q\x03Rq\x04c__builtin__\n__main__\nU\x08<lambda>q\x05NN}q\x06tq\x07Rq\x08.'

Ответ 2

Вместо этого вам придется использовать действительную функцию, которая является импортируемой (не вложенной внутри другой функции):

import cPickle as pickle
from numpy import sin, cos, array
def tmp(x):
    return sin(x)+cos(x)
test = array([[tmp,tmp],[tmp,tmp]],dtype=object)
pickle.dump( test, open('test.lambda','w') )

Объект функции все еще может быть выражен выражением lambda, но только если впоследствии вы получите результирующий объект функции с тем же именем:

tmp = lambda x: sin(x)+cos(x)
tmp.__name__ = 'tmp'
test = array([[tmp, tmp], [tmp, tmp]], dtype=object)

потому что pickle хранит только модуль и имя для объекта функции; в приведенном выше примере tmp.__module__ и tmp.__name__ теперь указывают прямо в место, где один и тот же объект можно найти снова при распаковке.

Ответ 3

Существует еще одно решение: определите функции как строки, pickle/un-pickle, затем используйте eval, ex:

import cPickle as pickle
from numpy import sin, cos, array
tmp = "lambda x: sin(x)+cos(x)"
test = array([[tmp,tmp],[tmp,tmp]],dtype=object)
pickle.dump( test, open('test.lambda','w') )
mytmp = array([[eval(x) for x in l] for l in pickle.load(open('test.lambda','r'))])
print mytmp
# yields : [[<function <lambda> at 0x00000000033D4DD8>
#            <function <lambda> at 0x00000000033D4E48>]
#           [<function <lambda> at 0x00000000033D4EB8>
#            <function <lambda> at 0x00000000033D4F28>]]

Это может быть более удобным для других решений, потому что маринованное представление будет полностью автономным без использования внешних зависимостей.