Ich versuche, Python-Funktionen (Code + Closures) zu serialisieren und später wieder zu installieren. Ich verwende den Code am Ende dieses Posts.Kann ich eine Funktion wiederherstellen, deren Cycle-Zyklen in Python enthalten sind?
Dies ist sehr flexibler Code. Es ermöglicht die Serialisierung und Deserialisierung von inneren Funktionen und Funktionen, die Verschlüsse, wie zum Beispiel diejenigen, die ihren Kontext müssen wieder eingesetzt werden:
def f1(arg):
def f2():
print arg
def f3():
print arg
f2()
return f3
x = SerialiseFunction(f1(stuff)) # a string
save(x) # save it somewhere
# later, possibly in a different process
x = load() # get it from somewhere
newf2 = DeserialiseFunction(x)
newf2() # prints value of "stuff" twice
Diese Anrufe auch dann funktionieren, wenn es Funktionen in der Schließung Ihrer Funktion sind, Funktionen in ihren Schließungen und so weiter (wir haben eine Grafik von Schließungen, wo Schließungen Funktionen enthalten, die Schließungen haben, die mehr Funktionen enthalten, und so weiter).
Es stellt sich jedoch heraus, dass diese Graphen Zyklen enthalten:
def g1():
def g2():
g2()
return g2()
g = g1()
Wenn ich g2
‚s Schließung aussehen (via g
), kann ich g2
darin sehen:
>>> g
<function g2 at 0x952033c>
>>> g.func_closure[0].cell_contents
<function g2 at 0x952033c>
Dies verursacht ein ernsthaftes Problem, wenn ich versuche, die Funktion zu deserialisieren, da alles unveränderlich ist. Was muss ich tun, um die Funktion zu machen newg2
:
newg2 = types.FunctionType(g2code, globals, closure=newg2closure)
wo newg2closure
wird wie folgt erstellt:
newg2closure = (make_cell(newg2),)
was natürlich nicht getan werden kann; Jede Codezeile beruht auf der anderen. Zellen sind unveränderlich, Tupel sind unveränderlich, Funktionstypen sind unveränderlich.
Also was ich herausfinden will ist, gibt es eine Möglichkeit, newg2
oben zu erstellen? Gibt es eine Möglichkeit, ein Objekt vom Funktionstyp zu erstellen, in dem das Objekt in einem eigenen Abschlussdiagramm erwähnt wird?
Ich benutze Python 2.7 (ich bin auf App Engine, so kann ich nicht zu Python 3 gehen).
Als Referenz meine Serialisierungsfunktionen:
def SerialiseFunction(aFunction):
if not aFunction or not isinstance(c, types.FunctionType):
raise Exception ("First argument required, must be a function")
def MarshalClosureValues(aClosure):
logging.debug(repr(aClosure))
lmarshalledClosureValues = []
if aClosure:
lclosureValues = [lcell.cell_contents for lcell in aClosure]
lmarshalledClosureValues = [
[marshal.dumps(litem.func_code), MarshalClosureValues(litem.func_closure)] if hasattr(litem, "func_code")
else [marshal.dumps(litem)]
for litem in lclosureValues
]
return lmarshalledClosureValues
lmarshalledFunc = marshal.dumps(aFunction.func_code)
lmarshalledClosureValues = MarshalClosureValues(aFunction.func_closure)
lmoduleName = aFunction.__module__
lcombined = (lmarshalledFunc, lmarshalledClosureValues, lmoduleName)
retval = marshal.dumps(lcombined)
return retval
def DeserialiseFunction(aSerialisedFunction):
lmarshalledFunc, lmarshalledClosureValues, lmoduleName = marshal.loads(aSerialisedFunction)
lglobals = sys.modules[lmoduleName].__dict__
def make_cell(value):
return (lambda x: lambda: x)(value).func_closure[0]
def UnmarshalClosureValues(aMarshalledClosureValues):
lclosure = None
if aMarshalledClosureValues:
lclosureValues = [
marshal.loads(item[0]) if len(item) == 1
else types.FunctionType(marshal.loads(item[0]), lglobals, closure=UnmarshalClosureValues(item[1]))
for item in aMarshalledClosureValues if len(item) >= 1 and len(item) <= 2
]
lclosure = tuple([make_cell(lvalue) for lvalue in lclosureValues])
return lclosure
lfunctionCode = marshal.loads(lmarshalledFunc)
lclosure = UnmarshalClosureValues(lmarshalledClosureValues)
lfunction = types.FunctionType(lfunctionCode, lglobals, closure=lclosure)
return lfunction
Dies ist eine super interessante Frage und ich weiß nicht genug, um es zu beantworten. Würde es überhaupt helfen, in den CAPI zu gehen? Ich habe Leute gesehen, die Verschlüsse so vor aa ändern http://pythondoeshwhat.blogspot.com/2012/11/back-porting-non-local.html Natürlich, wenn ich diesen Code laufen lasse, komme ich gerade mit ein segfault, also bin ich mir nicht sicher, wie echt diese Verbindung ist. –
Ich habe den C-Code für Python gelesen, frustrierend könnte man das auf dieser Ebene machen - alle Objekte sind änderbar - aber ich kann nicht hinein. Ich ziele auf appengine, also denke ich, dass alles, was ich tue, reine Python sein muss. –
Ein Hinweis: Sie können nicht Ihre eigenen Objekte anstelle von Zellen ersetzen. Ich habe es versucht :-) . Die c-Implementierung von types.FunctionType() überprüft sorgfältig, dass Sie ein Tupel von Zellen übergeben haben, wobei keine Tipp-Eingabe erlaubt ist. –