126 votes

Existe-t-il un moyen simple de récupérer une fonction python (ou de sérialiser son code) ?

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

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()

0 votes

Ce serait tellement plus cool que toutes les classes de sérialisation et d'API de REST.

136voto

Brian Points 48423

Vous pourriez sérialiser le bytecode de la fonction et ensuite le reconstruire sur l'appelant. Le site maréchal peut être utilisé pour sérialiser des objets de code, qui peuvent ensuite être réassemblés dans une fonction, par exemple :

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

Puis dans le processus distant (après avoir transféré la chaîne de code) :

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 (ou de tout autre bytecode python d'ailleurs) peut ne pas être compatible avec les principales versions de python.

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

  • Si la fonction fait référence à des éléments 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 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 génératrices.

1 votes

Dans Python 2.5, le module "new" est déprécié. 'new.function' devrait être remplacé par 'types.FunctionType', après un 'import types', je crois.

0 votes

@EOL : Bon point - j'ai mis à jour le code pour utiliser le module types à la place.

3 votes

Merci. C'est exactement ce que je cherchais. D'après quelques tests superficiels, cela fonctionne tel quel pour les générateurs.

47voto

Josh Rosen Points 2540

Vérifiez Aneth qui étend la bibliothèque pickle 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 à des objets dans la fermeture de la fonction :

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

2 votes

Dill fait aussi un très bon travail pour récupérer le code source des fonctions et des lambdas et le sauvegarder sur le disque, si vous préférez cela au décapage des objets.

0 votes

Ça marche. Et c'est aussi une solution immédiate, elle fonctionne directement après l'importation, je n'ai pas eu à modifier d'autre code autour de pickle.

0 votes

Il sauvegarde également les globales dans la fonction !

14voto

RichieHindle Points 98544

Pyro est capable de faire cela pour vous .

0 votes

Je dois m'en tenir à la bibliothèque standard pour ce projet particulier.

22 votes

Mais ça ne veut pas dire que tu ne peux pas regardez le code de Pyro pour voir comment c'est fait :)

5 votes

@AaronDigulla- vrai, mais il convient de mentionner qu'avant de lire une seule ligne du code publié par quelqu'un d'autre, vous devez toujours vérifier la licence du logiciel. Lire le code de quelqu'un d'autre et réutiliser les idées sans citer la source ou adhérer aux contraintes de licence/copie pourrait être considéré comme du plagiat et/ou une violation du droit d'auteur dans de nombreux cas.

12voto

Aaron Digulla Points 143830

Le moyen le plus simple est probablement inspect.getsource(object) (voir le inspecter le module ) qui renvoie une chaîne de caractères contenant le code source d'une fonction ou d'une méthode.

0 votes

Cela semble bon, sauf que le nom de la fonction est explicitement défini dans le code, ce qui est légèrement problématique. Je pourrais enlever la première ligne du code, mais cela peut être cassé en faisant quelque chose comme "def \/n func() :". Je pourrais associer le nom de la fonction à la fonction elle-même, mais je n'aurais aucune garantie que les noms n'entrent pas en collision, ou je devrais placer la fonction dans un wrapper, ce qui n'est toujours pas la solution la plus propre, mais qui pourrait être nécessaire.

1 votes

Notez que le module inspect demande simplement à la fonction où elle a été définie, puis lit les lignes du fichier de code source - ce qui n'est pas très sophistiqué.

1 votes

Vous pouvez trouver le nom de la fonction en utilisant son attribut .__name__. Vous pouvez faire un remplacement regex sur ^def \s *{nom} \s *( et donnez-lui le nom que vous voulez. Ce n'est pas infaillible, mais cela fonctionnera pour la plupart des choses.

7voto

kurczak Points 638

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 puisqu'il récupère la source de l'objet à partir de .py de sorte que seules les fonctions définies avant l'exécution peuvent être récupérées comme source.

Et si vos fonctions sont de toute façon placées dans des fichiers, pourquoi ne pas donner au récepteur un accès à ces fichiers et ne transmettre que les noms de modules et de fonctions.

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

Edit : le marshal La solution semble également très intelligente, je ne savais pas que l'on pouvait sérialiser autre chose que les modules intégrés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X