Может ли 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
Так, что происходит? Или, скорее, каков предел маринования лямбд?
[ПРАВИТЬ] Я думаю, я знаю, почему этот код работает. Я забыл (извините!) Я использую Python без стека, который имеет форму микропотоков, называемых тасклетами, выполняющими функцию. Эти тасклеты могут быть остановлены, зарезаны, не зарезаны и продолжены, поэтому я предполагаю (спросили в списке рассылки без стеков), что он также обеспечивает способ выбора тел функций.
3 ответа
Да, 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
Питон может мариновать лямбды. Мы рассмотрим Python 2 и 3 отдельно, так как реализация pickle различна в разных версиях Python.
- Python 2.7
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
Модуль поддерживает травление следующих дополнительных типов:
>> 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
Теперь давайте попробуем засолить лямбду с помощью 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 Но мы еще не импортировали его! Эта реализация 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
или cloudpickle и используйте его вместо собственного модуля pickle.
>> import dill
>> dill.loads(dill.dumps(lambda x:x))
<function __main__.<lambda>>
Я надеюсь, что это очищает все сомнения.
Нет, 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
Не уверен, что ты сделал это удалось...
Хотя это может быть очевидно, я хотел бы добавить другое возможное решение. Как вы, наверное, знаете, лямбда-функции - это просто анонимные объявления функций. Если у вас мало лямбд, которые используются только один раз, и это не добавит много шума в ваш код, вы можете просто назвать свою лямбду и передать ее имя (без скобок) следующим образом:
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. Но делайте это только в том случае, если это перевешивает дополнительный шум дополнительных функций.
Что сработало для меня (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 не нужны.
Установить укроп
$ 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
Укроп делают в основном так же, как маринад. Вы можете легко заменить словоpickle
с участием dill
.