2009-08-10 8 views
79

Próbuję przenieść funkcję przez połączenie sieciowe (używając asyncore). Czy istnieje prosty sposób na serializację funkcji Pythona (takiej, która w tym przypadku nie będzie miała żadnych skutków ubocznych) dla takiego transferu?Czy istnieje prosty sposób wybrania funkcji Pythona (lub inaczej serializowania jego kodu)?

Chciałbym idealnie chciałby mieć parę funkcje podobne do tych:

def transmit(func): 
    obj = pickle.dumps(func) 
    [send obj across the network] 

def receive(): 
    [receive obj from the network] 
    func = pickle.loads(s) 
    func() 
+3

masz wypróbowałeś swój kod? – monkut

Odpowiedz

105

Można serializować kod funkcji, a następnie zrekonstruować go na dzwoniącym. Moduł marshal można wykorzystać do serializacji obiektów kodu, które następnie można ponownie połączyć w funkcję. tj:

import marshal 
def foo(x): return x*x 
code_string = marshal.dumps(foo.func_code) 

Następnie w pilocie zdalnego procesu (po przeniesieniu code_string):

import marshal, types 

code = marshal.loads(code_string) 
func = types.FunctionType(code, globals(), "some_func_name") 

func(10) # gives 100 

Kilka Ostrzeżenia:

  • formatu marszałka (żadnego kodu bajtowego Pythona do tej sprawy) nie może być kompatybilnym między głównymi wersjami python.

  • Działa tylko w celu wdrożenia cpython.

  • Jeśli funkcja odwołuje się do globali (włączając importowane moduły, inne funkcje itp.), Które musisz odebrać, musisz je również serializować lub odtworzyć na zdalnej stronie. Mój przykład po prostu nadaje mu globalny obszar nazw zdalnego procesu.

  • Prawdopodobnie będziesz musiał zrobić jeszcze więcej, aby obsłużyć bardziej złożone przypadki, takie jak zamknięcie lub funkcje generatora.

+1

W Pythonie 2.5 "nowy" moduł jest przestarzały. "new.function" powinno zostać zastąpione przez "types.FunctionType", po "typach importu", jak sądzę. – EOL

+0

@EOL: Dobra uwaga - zaktualizowałem kod, aby korzystać z modułu typów. – Brian

+1

Dzięki. Właśnie tego szukałem. Opierając się na pobieżnych testach, działa on tak jak generator. –

10

Najprostszym sposobem jest prawdopodobnie inspect.getsource(object) (patrz inspect module), która zwraca ciąg znaków z kodu źródłowego za pomocą funkcji lub metoda.

+0

To wygląda dobrze, z tym wyjątkiem, że nazwa funkcji jest jawnie zdefiniowana w kodzie, co jest nieco problematyczne. Mogę usunąć pierwszą linię kodu, ale jest to łamliwe, robiąc coś takiego jak "def \/n func():". Mogę podać nazwę funkcji za pomocą samej funkcji, ale nie mam gwarancji, że nazwa się nie zderzy, lub musiałbym umieścić funkcję w opakowaniu, które nadal nie jest najczystszym rozwiązaniem, ale być może będzie musiał. –

+0

Należy zauważyć, że moduł inspekcyjny właśnie pyta o funkcję, gdzie został zdefiniowany, a następnie odczytuje w tych wierszach z pliku kodu źródłowego - mało wyrafinowany. –

+1

Możesz znaleźć nazwę funkcji za pomocą jej .__ nazwa__ atrybut. Możesz zrobić zamianę regex na^def \ s * {name} \ s * (i nadać jej dowolną nazwę, nie jest to niezawodne, ale zadziała dla większości rzeczy.) –

13

Pyro jest w stanie do this for you.

+0

Musiałbym trzymać się standardowej biblioteki dla tego konkretnego projektu –

+15

Ale to nie znaczy, że nie możesz * spojrzeć * na kod Pyro, żeby zobaczyć, jak to się robi :) –

+0

Łącze przerwane - oto Dokumentacja Pyro - http: //packages.python. org/Pyro4/ –

5

Wszystko zależy od tego, czy generowanie funkcji w czasie wykonywania lub nie:

Jeśli tak - inspect.getsource(object) nie będzie działać na dynamicznie generowanych funkcji, jak robi źródło obiektu od .py pliku, więc tylko funkcje zdefiniowane przed wykonaniem można pobrać jako źródło.

A jeśli twoje funkcje są umieszczone w plikach mimo to, dlaczego nie dać im dostępu do odbiornika i tylko przechodzić wokół nazw modułów i funkcji.

Jedynym rozwiązaniem dla dynamicznie tworzonych funkcji, które mogę wymyślić jest skonstruowanie funkcji jako ciąg przed transmisją, przesłaniem źródła, a następnie eval() po stronie odbiorcy.

Edit: rozwiązanie marshal również wygląda bardzo inteligentny, nie wiedziała coś innego thatn Zabudowy można szeregować

1

Podstawowe funkcje używane dla tego modułu obejmuje zapytanie, plus można dostać najlepszą kompresję nad drut; zobacz pouczający kod źródłowy:

y_serial.moduł py :: obiekt magazynu Python z SQLite

"Serializacja + persistance :: w kilku linijkach kodu, kompresuj i dodawaj adnotacje do obiektów Python w SQLite, a następnie pobieraj je chronologicznie według słów kluczowych bez żadnego SQL. moduł dla bazy danych do przechowywania danych bez schematu. "

http://yserial.sourceforge.net

25

Wyjazd Dill, która rozciąga ogórkowy biblioteki Pythona wspierać większą różnorodność typów, w tym funkcji:

>>> import dill as pickle 
>>> def f(x): return x + 1 
... 
>>> g = pickle.dumps(f) 
>>> f(1) 
2 
>>> pickle.loads(g)(1) 
2 

Wspiera również referencje do obiektów w zamknięciu danej funkcji:

>>> def plusTwo(x): return f(f(x)) 
... 
>>> pickle.loads(pickle.dumps(plusTwo))(1) 
3 
+1

Koper także robi całkiem dobrą robotę, pobierając kod źródłowy z funkcji i lambdas i zapisując je na dysku, jeśli wolałbyś, aby nie został on wytrawiony. –

Powiązane problemy