Próbuję serializować funkcje Pythona (kod + zamknięcia) i przywrócić je później. Używam kodu u dołu tego wpisu.Czy mogę przywrócić funkcję, której zamknięcie zawiera cykle w języku Python?
To jest bardzo elastyczny kod. Pozwala to szeregowanie i deserialisation funkcji wewnętrznych i funkcje, które są zamknięcia, takie jak te, które potrzebują ich kontekst zostać przywrócone:
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
te połączenia będą działać, nawet jeśli istnieją funkcje w zamknięciu swojej funkcji, funkcjonuje w ich zamknięciach i tak dalej (mamy wykres zamknięć, w których zamknięcia zawierają funkcje, które mają zamknięcia, które zawierają więcej funkcji itd.).
Jednak okazuje się, że te wykresy mogą zawierać cykle:
def g1():
def g2():
g2()
return g2()
g = g1()
Jeśli patrzę na zamknięciu g2
„s (poprzez g
), widzę g2
w nim:
>>> g
<function g2 at 0x952033c>
>>> g.func_closure[0].cell_contents
<function g2 at 0x952033c>
To powoduje poważny problem, gdy próbuję deserializować funkcję, ponieważ wszystko jest niezmienne. Co muszę zrobić, aby funkcję newg2
:
newg2 = types.FunctionType(g2code, globals, closure=newg2closure)
gdzie newg2closure
tworzony jest w następujący sposób:
newg2closure = (make_cell(newg2),)
co oczywiście nie może być wykonana; każda linia kodu opiera się na drugiej. Komórki są niezmienne, krotki są niezmienne, typy funkcji są niezmienne.
Więc próbuję się dowiedzieć, czy istnieje sposób na stworzenie powyższego newg2
? Czy istnieje sposób, w jaki mogę utworzyć obiekt typu funkcji, w którym obiekt jest wymieniony na jego własnym wykresie zamknięcia?
Używam Pythona 2.7 (jestem na App Engine, więc nie mogę przejść do Pythona 3).
Dla porównania, moje funkcje serializacji:
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
To jest bardzo ciekawe pytanie i nie wiem wystarczająco dużo, aby na nie odpowiedzieć. Czy pomogłoby to w ogóle w C api? Widziałem, jak ludzie zmieniają zamknięcia w ten sposób przed a la http://pythondoeswhat.blogspot.com/2012/11/back-porting-non-local.html Oczywiście, kiedy uruchamiam ten kod, po prostu kończę z a segfault, więc nie jestem pewien, jak legalne jest to łącze. –
Czytałem kod C dla Pythona, frustrująco możesz sprawić, żeby działało na tym poziomie - wszystkie obiekty są zmienne - ale nie mogę się tam dostać. Jestem nastawiony na appengine, więc myślę, że wszystko co robię musi być czystym pytonem. –
Uwaga: Nie można zastąpić własnych obiektów zamiast komórek. Próbowałem :-) . Implementacja type.FunctionType() sprawdza dokładnie, czy przekazałeś mu krotkę komórek, bez pisania na maszynie. –