Подтвердить что ты не робот

Может ли Python раскрыть лямбда-функции?

Я читал в ряде потоков, что Python pickle/cPickle не может расчехлять лямбда-функции. Однако следующий код работает, используя 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

Так что происходит? Или, точнее, каков предел травля лямбдов?

[EDIT] Я думаю, я знаю, почему этот код работает. Я забыл (извините!) Я запускаю стековый пейтон, который имеет форму микропотоков, называемых tasklets, выполняющих функцию. Эти цепочки можно остановить, мариновать, рассыпать и продолжить, поэтому я предполагаю (заданный в списке рассылки без стекол), что он также обеспечивает способ рассортировки тел функций.

4b9b3361

Ответ 1

Да, python может расчехлять лямбда-функции... но только если у вас есть что-то, что использует copy_reg для регистрации как для расчёта лямбда-функций - пакет dill загружает copy_reg вам нужно в реестр рассола для вас, когда вы 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>

получить укроп здесь: https://github.com/uqfoundation

Ответ 2

Нет, Python не может расчехлять лямбда-функции:

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

Не уверен, что вы сделали, что это удалось...

Ответ 3

Питон может мариновать лямбды. Мы рассмотрим Python 2 и 3 отдельно, поскольку реализация pickle в разных версиях Python различна.

  • Python 2.7

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

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

Для выбора пользовательских типов Python предоставляет модуль copy_reg для регистрации наших функций. Вы можете прочитать больше об этом здесь. По умолчанию модуль copy_reg поддерживает copy_reg следующих следующих дополнительных типов:

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

Теперь тип lambda функций - это types.FunctionType. Однако встроенная функция для функции этого типа function: <function pickle.save_global> не может сериализовать лямбда-функции. Поэтому все сторонние библиотеки, такие как dill, cloudpickle и т.д. переопределить встроенный метод сериализации лямбда - функции с некоторой дополнительной логикой. Давай импортируем dill и посмотрим, что он делает.

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

Теперь давайте попробуем засолить лямбда-функцию.

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

Оно работает!!

В Python 2 у нас есть две версии 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

Теперь давайте попробуем cPickle лямбду с помощью C-реализации cPickle.

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

Что пошло не так? Давайте посмотрим таблицу отправки cPickle.

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

Реализация pickle и cPickle отличается. Importing укропа работает только версия Python для pickle. Недостаток использования pickle вместо cPickle заключается в том, что он может быть в 1000 раз медленнее, чем cPickle.

  • Python 3.6

В Python 3 нет модуля с именем cPickle. Вместо этого у нас есть pickle который по умолчанию также не поддерживает выборку lambda функций. Давайте посмотрим на таблицу отправки:

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

Подождите. Я попытался посмотреть dispatch_table для pickle не _pickle. _pickle - альтернативная и более быстрая реализация C для pickle. Но мы еще не импортировали его! Эта реализация C импортируется автоматически, если она доступна, в конце чистого модуля 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

Мы все еще остаемся с вопросом засолки лямбд в Python 3. Ответ: вы НЕ МОЖЕТЕ использовать нативный pickle или _pickle. Вам нужно будет импортировать dill или облачный рассол и использовать его вместо собственного модуля засолки.

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

Я надеюсь, что это очищает все сомнения.

Ответ 4

у меня сработало (windows 10, python 3.7) передача функции вместо лямбда-функции:

def merge(x):
    return Image.merge("RGB", x.split()[::-1])

transforms.Lambda(merge)

вместо:

transforms.Lambda(lambda x: Image.merge("RGB", x.split()[::-1]))

не нужно укропа или cPickel.

Ответ 5

Хотя это может быть очевидно, я хотел бы добавить другое возможное решение. Как вы, наверное, знаете, лямбда-функции - это просто анонимные объявления функций. Если у вас мало лямбд, которые используются только один раз, и это не добавит много шума в ваш код, вы можете просто назвать свою лямбду и передать ее имя (без скобок) следующим образом:

import cPickle as pickle

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


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

Имя также добавляет больше семантики, и вам не понадобится дополнительная зависимость, такая как Dill. Но делайте это только в том случае, если это перевешивает дополнительный шум дополнительных функций.

Ответ 6

Установить укроп

$ pip install dill

Коснитесь файла

touch yeah.p

Теперь запустите этот скрипт на 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