web-dev-qa-db-fra.com

Existe-t-il un moyen facile de décaper une fonction python (ou autrement sérialiser son code)?

J'essaie de transférer une fonction sur une connexion réseau (en utilisant asyncore). Existe-t-il un moyen facile de sérialiser une fonction python (celle qui, dans ce cas au moins, n'aura pas d'effets secondaires) pour un transfert comme celui-ci?

J'aimerais idéalement avoir une paire de fonctions similaires à celles-ci:

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

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()
90
Michael Fairley

Vous pouvez sérialiser le bytecode de la fonction, puis le reconstruire sur l'appelant. Le module marshal peut être utilisé pour sérialiser des objets de code, qui peuvent ensuite être réassemblés en fonction. c'est à dire:

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

Puis dans le processus distant (après avoir transféré code_string):

import marshal, types

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

func(10)  # gives 100

Quelques mises en garde:

  • le format de marshal (n'importe quel python bytecode d'ailleurs) peut ne pas être compatible entre les principales versions python versions).

  • Ne fonctionnera que pour l'implémentation de cpython.

  • Si la fonction fait référence à des globaux (y compris des modules importés, d'autres fonctions, etc.) que vous devez récupérer, vous devrez également les sérialiser ou les recréer du côté distant. Mon exemple lui donne simplement l'espace de noms global du processus distant.

  • Vous devrez probablement en faire un peu plus pour prendre en charge des cas plus complexes, comme les fermetures ou les fonctions de générateur.

113
Brian

Découvrez Dill , qui étend la bibliothèque de cornichons de Python pour prendre en charge une plus grande variété de types, y compris les fonctions:

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

Il prend également en charge les références aux objets dans la fermeture de la fonction:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3
36
Josh Rosen

Pyro est capable de faire cela pour vous .

14
RichieHindle

Le moyen le plus simple est probablement inspect.getsource(object) (voir inspect module ) qui retourne une chaîne avec le code source d'une fonction ou d'une méthode.

10
Aaron Digulla

Tout dépend si vous générez la fonction au moment de l'exécution ou non:

Si vous le faites - inspect.getsource(object) ne fonctionnera pas pour les fonctions générées dynamiquement car il obtient la source de l'objet à partir du fichier .py, Donc seules les fonctions définies avant l'exécution peuvent être récupérées en tant que source.

Et si vos fonctions sont quand même placées dans des fichiers, pourquoi ne pas leur donner accès au récepteur et ne transmettre que les noms des modules et des fonctions.

La seule solution pour les fonctions créées dynamiquement à laquelle je peux penser est de construire la fonction comme une chaîne avant la transmission, de transmettre la source, puis de la eval() du côté récepteur.

Edit: la solution marshal semble également assez intelligente, je ne savais pas que vous pouvez sérialiser quelque chose d'autre que cela intégré

6
kurczak

Le package cloud (cloud d'installation pip) peut décaper du code arbitraire, y compris les dépendances. Voir https://stackoverflow.com/a/16891169/1264797 .

5
stevegt
code_string = '' '
 def foo (x): 
 return x * 2 
 def bar (x): 
 return x ** 2 
 '' '
 
 obj = pickle.dumps (code_string) 
 

À présent

 exec (pickle.loads (obj)) 
 
 foo (1) 
> 2 
 bar (3) 
> 9 
1
Yanni Papadakis

Les fonctions de base utilisées pour ce module couvrent votre requête, plus vous obtenez la meilleure compression sur le fil; voir le code source instructif:

module y_serial.py :: entrepôt Python objets avec SQLite

"Sérialisation + persistance :: en quelques lignes de code, compressez et annotez Python objets dans SQLite; puis récupérez-les chronologiquement par mots-clés sans SQL. Module" standard "le plus utile pour une base de données pour stocker des données sans schéma. "

http://yserial.sourceforge.net

1
code43