Python сериализует лексические закрытия?

Есть ли способ сериализации лексического закрытия в Python с использованием стандартной библиотеки? рассол и маршал, похоже, не работают с лексическими закрытиями. Меня не волнуют детали двоичной и сериализации строк и т. Д., Она просто должна работать. Например:

def foo(bar, baz) : def closure(waldo) : return baz * waldo return closure 

Я хотел бы просто иметь возможность сбрасывать экземпляры закрытия в файл и читать их обратно.

Редактирование: один относительно очевидный способ, которым это можно решить, – это некоторые рефлексивные хаки для преобразования лексических замыканий в объекты класса и наоборот. Затем можно было бы преобразовать в классы, сериализовать, unserialize, конвертировать обратно в закрытие. Heck, учитывая, что Python утка напечатана, если вы перегрузили оператор вызова функции класса, чтобы он выглядел как функция, вам даже не нужно было бы преобразовывать его обратно в закрытие, а код, использующий его, не знал бы разница. Если у вас есть какие-либо гуру API-анализа Python, пожалуйста, говорите.

5 Solutions collect form web for “Python сериализует лексические закрытия?”

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

 class foo(object): def __init__(self, bar, baz): self.baz = baz def __call__(self,waldo): return self.baz * waldo 

С другой стороны, хак, который преобразовал замыкание в экземпляр нового класса, созданного во время выполнения, не сработает из-за того, что pickle имеет дело с классами и экземплярами. pickle не хранит классы; только имя модуля и имя класса. При чтении экземпляра или класса он пытается импортировать модуль и найти в нем нужный класс. Если вы использовали класс, созданный «на лету», вам не повезло.

PiCloud выпустила раскройщик с открытым исходным кодом (LGPL), который может работать с закрытием функции и гораздо более полезным материалом. Он может использоваться независимо от инфраструктуры облачных вычислений – это просто обычный pickler. Здесь описывается весь shebang, и вы можете загрузить код через «pip install cloud». Во всяком случае, он делает то, что вы хотите. Давайте продемонстрируем, что, замачивая замыкание:

 import pickle from StringIO import StringIO import cloud # generate a closure def foo(bar, baz): def closure(waldo): return baz * waldo return closure closey = foo(3, 5) # use the picloud pickler to pickle to a string f = StringIO() pickler = cloud.serialization.cloudpickle.CloudPickler(f) pickler.dump(closey) #rewind the virtual file and reload f.seek(0) closey2 = pickle.load(f) 

Теперь у нас есть closey , исходное закрытие и closey2 , которое было восстановлено из строковой сериализации. Давайте проверим их.

 >>> closey(4) 20 >>> closey2(4) 20 

Красивый. Модуль – чистый python – вы можете открыть его и легко увидеть, что заставляет магию работать. (Ответ – много кода.)

Да! Я получил (по крайней мере, я думаю), то есть более общую проблему расчёта функции. Python так замечателен :), я узнал об этом, хотя функция dir () и несколько веб-поисков. Также замечательно, что он [надеюсь] решил, мне тоже нужно.

Я не проводил много испытаний на то, насколько надежна эта вещь co_code (вложенные fcns и т. Д.), И было бы неплохо, если бы кто-то мог посмотреть, как подключить Python, чтобы функции могли автоматически протравливаться (например, иногда они могут быть замыкающие аргументы).

Модуль Cython _pickle_fcn.pyx

 # -*- coding: utf-8 -*- cdef extern from "Python.h": object PyCell_New(object value) def recreate_cell(value): return PyCell_New(value) 

Файл Python

 #!/usr/bin/env python # -*- coding: utf-8 -*- # author gatoatigrado [ntung.com] import cPickle, marshal, types import pyximport; pyximport.install() import _pickle_fcn def foo(bar, baz) : def closure(waldo) : return baz * waldo return closure # really this problem is more about pickling arbitrary functions # thanks so much to the original question poster for mentioning marshal # I probably wouldn't have found out how to serialize func_code without it. fcn_instance = foo("unused?", -1) code_str = marshal.dumps(fcn_instance.func_code) name = fcn_instance.func_name defaults = fcn_instance.func_defaults closure_values = [v.cell_contents for v in fcn_instance.func_closure] serialized = cPickle.dumps((code_str, name, defaults, closure_values), protocol=cPickle.HIGHEST_PROTOCOL) code_str_, name_, defaults_, closure_values_ = cPickle.loads(serialized) code_ = marshal.loads(code_str_) closure_ = tuple([_pickle_fcn.recreate_cell(v) for v in closure_values_]) # reconstructing the globals is like pickling everything :) # for most functions, it's likely not necessary # it probably wouldn't be too much work to detect if fcn_instance global element is of type # module, and handle that in some custom way # (have the reconstruction reinstantiate the module) reconstructed = types.FunctionType(code_, globals(), name_, defaults_, closure_) print(reconstructed(3)) 

веселит,
Николас

EDIT – для реальных случаев необходима более надежная глобальная обработка. fcn.func_code.co_names перечисляет глобальные имена.

Recipe 500261: Named Tuples содержит функцию, которая определяет класс «на лету». И этот класс поддерживает травление.

Вот суть:

 result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') 

В сочетании с предложением @Greg Ball создать новый класс во время выполнения, он может ответить на ваш вопрос.

 #!python import marshal, pickle, new def dump_func(f): if f.func_closure: closure = tuple(c.cell_contents for c in f.func_closure) else: closure = None return marshal.dumps(f.func_code), f.func_defaults, closure def load_func(code, defaults, closure, globs): if closure is not None: closure = reconstruct_closure(closure) code = marshal.loads(code) return new.function(code, globs, code.co_name, defaults, closure) def reconstruct_closure(values): ns = range(len(values)) src = ["def f(arg):"] src += [" _%d = arg[%d]" % (n, n) for n in ns] src += [" return lambda:(%s)" % ','.join("_%d"%n for n in ns), ''] src = '\n'.join(src) try: exec src except: raise SyntaxError(src) return f(values).func_closure if __name__ == '__main__': def get_closure(x): def the_closure(a, b=1): return a * x + b, some_global return the_closure f = get_closure(10) code, defaults, closure = dump_func(f) dump = pickle.dumps((code, defaults, closure)) code, defaults, closure = pickle.loads(dump) f = load_func(code, defaults, closure, globals()) some_global = 'some global' print f(2) 
 
Interesting Posts for Van-Lav

Самонастраивающийся Python? Как я могу перенаправить все операторы печати внутри функции, не касаясь sys.stdout?

Pandas join issue: столбцы перекрываются, но суффикс не указан

Прогнозирование ARMA вне выборки с помощью statsmodels

Есть ли достойный способ создания конструктора копирования в python?

Как я могу захватить возвращаемое значение shutil.copy () в Python (в DOS)?

Apache SetEnv работает не так, как ожидалось, с mod_wsgi

найти местоположение пиков в спектре numpy

Имеет ли Django исключение для немедленного ответа HTTP?

Сделать словарь в Python из входных значений

Случайно установлен python 3.5 vs. python3.5: Это плохо?

Разница между «if x» и «if x is not None»

Как сгладить список, чтобы вернуть новый список со всеми элементами?

assertRaises не удается, даже вызываемый вызывает требуемое исключение (python, unitest)

проверьте, имеет ли моя команда «import pdb» в emacs / git?

Есть ли эквивалент JavaScript или jQuery для встроенной функции «sum» Python?

Python - лучший язык программирования в мире.