Introspection des fonctions imbriquées (locales) d'une fonction donnée en Python
-
22-07-2019 - |
Question
Étant donné la fonction
def f():
x, y = 1, 2
def get():
print 'get'
def post():
print 'post'
puis-je accéder aux fonctions locales get () et post () de manière à ce que je puisse les appeler? Je cherche une fonction qui fonctionnera comme avec la fonction f () définie ci-dessus:
>>> get, post = get_local_functions(f)
>>> get()
'get'
Je peux accéder aux objets de code pour les fonctions locales telles que
import inspect
for c in f.func_code.co_consts:
if inspect.iscode(c):
print c.co_name, c
ce qui entraîne
get <code object get at 0x26e78 ...>
post <code object post at 0x269f8 ...>
mais je ne vois pas comment obtenir les objets de fonction appelables. Est-ce même possible?
Merci de votre aide,
Will.
La solution
Vous êtes sur le point de le faire - il manque juste le nouveau
module:
import inspect
import new
def f():
x, y = 1, 2
def get():
print 'get'
def post():
print 'post'
for c in f.func_code.co_consts:
if inspect.iscode(c):
f = new.function(c, globals())
print f # Here you have your function :].
Mais pourquoi diable dérange-t-il? N'est-ce pas plus facile d'utiliser la classe? L’instanciation ressemble quand même à un appel de fonction.
Autres conseils
Vous pouvez renvoyer des fonctions comme n'importe quel autre objet Python:
def f():
x, y = 1, 2
def get():
print 'get'
def post():
print 'post'
return (get, post)
get, post = f()
J'espère que ça aide!
Notez cependant que si vous souhaitez utiliser vos variables 'x' et 'y' dans get () ou post (), vous devez en faire une liste.
Si vous faites quelque chose comme ça:
def f():
x = [1]
def get():
print 'get', x[0]
x[0] -= 1
def post():
print 'post', x[0]
x[0] += 1
return (get, post)
get1, post1 = f()
get2, post2 = f()
get1 et post1 référenceront une liste 'x' différente de celle de get2 et post2.
Vous pouvez utiliser exec pour exécuter les objets de code. Par exemple, si vous avez défini f comme ci-dessus, alors
exec(f.func_code.co_consts[3])
donnerait
get
en sortie.
Les objets fonction internes n'existent pas avant l'exécution de la fonction f (). Si vous voulez les obtenir, vous devrez les construire vous-même. C’est certainement non trivial, car il peut s’agir de fermetures capturant des variables du champ de la fonction et nécessitant de toute façon des fouilles dans des objets qui devraient probablement être considérés comme des détails d’implémentation de l’interprète.
Si vous souhaitez collecter les fonctions avec moins de répétition, nous vous recommandons l'une des approches suivantes:
a) Il suffit de placer les fonctions dans une définition de classe et de renvoyer une référence à cette classe. Un ensemble de fonctions connexes auxquelles on accède par nom sent terriblement comme une classe.
b) Créez une sous-classe dict qui possède une méthode d’enregistrement des fonctions et utilisez-la comme décoratrice.
Le code correspondant à ceci ressemblerait à ceci:
class FunctionCollector(dict):
def register(self, func):
self[func.__name__] = func
def f():
funcs = FunctionCollector()
@funcs.register
def get():
return 'get'
@funcs.register
def put():
return 'put'
return funcs
c) Rechercher dans les sections locales () et filtrer la fonction avec inspect.isfunction. (généralement pas une bonne idée)