Как преобразовать объект `lambda` в объект`function` в Python?

Я имел дело с ошибками, касающимися lambda функции и их неспособность быть pickled, Я часто использую lambda функции "на лету", так как одно время используют функции, и это значительно снижает производительность моего рабочего процесса, когда мне приходится индивидуально воссоздавать простые лямбда-функции в функциональной форме для использования с травлением.

Есть ли способ конвертировать lambda и все его аргументы в function объект в Python 3.6.1 ?

lambda_func = lambda x: x.split(" ")
def func(x):
    return x.split(" ")
input_string = "Darth Plagueis was a Dark Lord of the Sith"

# Function Version
func(input_string)
# ['Darth', 'Plagueis', 'was', 'a', 'Dark', 'Lord', 'of', 'the', 'Sith']
lambda_func(input_string)
# ['Darth', 'Plagueis', 'was', 'a', 'Dark', 'Lord', 'of', 'the', 'Sith']

def lambda2func(lambda_func):
    #...
    return func_version

0 ответов

pickleне сохраняет объект кода, только его имя. Однако присвоения функции имени недостаточно, чтобы сделать ее маринованной, потому чтоunpickleдесериализует его, импортировав имя. Естественно, это означает, что он должен быть импортирован средой выполнения unpickling. Функцииdefed с именами по этой причине все еще может не засолиться. Так что даже если бы вы могли преобразовать свои лямбды в именованные функции, это все равно не сработало бы:

>>> import pickle
>>> def foo():
...   def bar():
...     pass
...   return bar
...
>>> pickle.dumps(foo())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Can't pickle local object 'foo.<locals>.bar'

Вы говорите, почему не используете dill вместо этого

Я видел пример использования, когда укроп можно импортировать как рассол, что довольно круто, но сбивает с толку в коде, поскольку я использовал рассол и укроп для различных типов сериализации.

Но pickleФормат сериализации расширяется до любого внутреннего формата, который вам нравится -json, gzip, четный dill. Вы можете съесть свой торт и съесть его!

Давай попробуем с dill, потому что это упрощает:

import pickle
import dill
​
class DillPickle:
    def __init__(self, e):
        self.e = e
    def __getstate__(self):
        return dill.dumps(self.e)
    def __setstate__(self, state):
        self.e = dill.loads(state)

Pickle обычно сериализует состояние экземпляров настраиваемого класса, рекурсивно обрабатывая объект __dict__. При распаковке он может создать неинициализированный экземпляр и обновить его__dict__с сохраненными значениями. Если все его содержимое можно мариновать, это работает. Но в противном случае (как в случае с лямбдами) травление не удастся. Ноpickle модуль предоставляет интерфейс для переопределения этого поведения: __getstate__() для сериализации состояния объекта и __setstate__()для восстановления его из этого значения. Если возвращаемое значение__getstate__() можно мариновать (и __setstate__()реализовано), это работает. В этом случае мы возвращаем выбираемую байтовую строку, возвращаемуюdill.dumps().

Демонстрация:

>>> pickle.dumps(DillPickle(lambda x, y: x+y))
b'\x80\x03c__main__\nDillPickle\nq\x00)\x81q\x01C\xe7\x80\x03cdill._dill\n_create_function\nq\x00(cdill._dill\n_load_type\nq\x01X\x08\x00\x00\x00CodeTypeq\x02\x85q\x03Rq\x04(K\x02K\x00K\x02K\x02KCC\x08|\x00|\x01\x17\x00S\x00q\x05N\x85q\x06)X\x01\x00\x00\x00xq\x07X\x01\x00\x00\x00yq\x08\x86q\tX\x1f\x00\x00\x00<ipython-input-18-48b9de2c6f55>q\nX\x08\x00\x00\x00<lambda>q\x0bK\x01C\x00q\x0c))tq\rRq\x0ec__builtin__\n__main__\nh\x0bNN}q\x0fNtq\x10Rq\x11.q\x02b.'
>>> pickle.loads(_).e('foo', 'bar')
'foobar'

Обратите внимание, что мы загрузили его pickleне dill! А у лямбды нет имени. Конечно,dill должен быть доступен для среды выполнения, чтобы распаковывать, но то же самое делает и остальная часть DillPickle класс, как и для любого настраиваемого типа, сериализованного с помощью pickle. dill теперь просто деталь реализации, используемая внутри DillPickle. Интерфейсpickle.

Вы даже можете сделать его вызываемым, поэтому вам не нужно добавлять .e сами.

class DillPickleCallable(DillPickle):
    def __call__(self, *args, **kwargs):
        return self.e(*args, **kwargs)
Другие вопросы по тегам