Python serialisieren lexikalische Verschlüsse?

Gibt es einen Weg, um eine lexikalische Schließung in Python mit der Standardbibliothek zu serialisieren? Gurke und Marschall erscheinen nicht mit lexikalischen Verschlüssen zu arbeiten. Ich kümmere mich nicht wirklich um die Details der binären vs. String Serialisierung, etc., es muss nur funktionieren. Beispielsweise:

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

Ich möchte nur in der Lage sein, Instanzen der Schließung zu einer Datei zu entleeren und sie zurück zu lesen.

Edit: Eine relativ offensichtliche Art und Weise, dass dies gelöst werden könnte, ist mit einigen Reflexionshacks, um lexikalische Verschlüsse in Klassenobjekte umzuwandeln und umgekehrt. Man könnte dann in Klassen umwandeln, serialisieren, unsialisieren, wieder in Schließungen umwandeln. Heck, da Python duck getippt ist, wenn du den Funktionsaufrufbetreiber der Klasse überlastet hast, um es wie eine Funktion auszusehen, würdest du es nicht einmal wirklich brauchen, um es wieder in eine Schließung umzuwandeln, und der Code, der ihn benutzt, würde es nicht wissen der Unterschied. Wenn irgendwelche Python Reflexion API Gurus sind da draußen, bitte sprechen Sie sich.

5 Solutions collect form web for “Python serialisieren lexikalische Verschlüsse?”

Wenn du einfach eine Klasse mit einer __call__ Methode benutzt __call__ , __call__ du alles reibungslos mit pickle .

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

Auf der anderen Seite würde ein Hack, der eine Schließung in eine Instanz einer neuen Klasse umgesetzt hat, die zur Laufzeit geschaffen wurde, nicht funktionieren, weil der pickle mit Klassen und Instanzen behandelt wird. pickle speichert keine Klassen; Nur ein Modulname und Klassenname. Beim Lesen einer Instanz oder Klasse versucht es, das Modul zu importieren und die gewünschte Klasse darin zu finden. Wenn du eine Klasse benutzt hast, die on-the-fly erstellt wurde, hast du kein Glück.

PiCloud hat einen Open-Source- (LGPL) Pickler veröffentlicht, der den Funktionsabschluss und eine ganze Menge nützlicheres Sachen verarbeiten kann. Es kann unabhängig von ihrer Cloud-Computing-Infrastruktur verwendet werden – es ist nur ein normaler Pickler. Die ganze Shebang ist hier dokumentiert, und du kannst den Code über 'pip install cloud' herunterladen. Jedenfalls tut es was du willst Lassen Sie uns zeigen, dass durch Beizen eine Schließung:

 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) 

Jetzt haben wir closey , die ursprüngliche closey2 und closey2 , die, die von einer string serialisierung wiederhergestellt wurde. Lass uns testen.

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

Schön. Das Modul ist reine Python-Sie können es öffnen und leicht sehen, was macht die magische Arbeit. (Die Antwort ist viel Code.)

Ja! Ich habe es (zumindest denke ich) – das heißt, das allgemeinere Problem des Beizens einer Funktion. Python ist so wunderbar :), fand ich das meiste davon aber die dir () – Funktion und ein paar Websuche. Auch wunderbar, um es [hoffentlich] gelöst zu haben, ich brauchte es auch

Ich habe nicht viel getestet, wie robust diese Co_code-Sache ist (verschachtelte fcns, etc.), und es wäre schön, wenn jemand nachschlagen könnte, wie man Python hängt, so dass Funktionen automatisch gebeizt werden können (zB könnten sie manchmal sein Verschluss args).

Cython-Modul _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-Datei

 #!/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)) 

Prost,
Nikolaus

EDIT – robusteres globales Handling ist für reale Fälle notwendig. Fcn.func_code.co_names listet globale Namen auf.

Rezept 500261: Named Tuples enthält eine Funktion, die eine Klasse on-the-fly definiert. Und diese Klasse unterstützt das Beizen.

Hier ist die Essenz:

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

Kombiniert mit @Greg Ball's Vorschlag , um eine neue Klasse zur Laufzeit zu schaffen, könnte es Ihre Frage beantworten.

 #!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) 
Python ist die beste Programmiersprache der Welt.