web-dev-qa-db-fra.com

Comment obtenir la valeur de retour lors de l'utilisation de Python exec sur l'objet code d'une fonction?

À des fins de test, je souhaite exécuter directement une fonction définie dans une autre fonction.

Je peux accéder à l'objet de code de la fonction enfant, via le code (func_code) de la fonction parent, mais lorsque je l'exécute, je n'obtiens aucune valeur de retour.

Existe-t-il un moyen d'obtenir la valeur de retour à partir du code exécuté?

15
user2433423

Oui, vous devez avoir l'affectation dans l'instruction exec:

>>> def foo():
...     return 5
...
>>> exec("a = foo()")
>>> a
5

Ceci n’est probablement pas pertinent pour votre cas, car il est utilisé dans des tests contrôlés, mais faites attention à utiliser exec avec une entrée définie par l’utilisateur.  

12
wnnmaw

Quelque chose comme ça peut marcher:

def outer():
    def inner(i):
        return i + 10


for f in outer.func_code.co_consts:
    if getattr(f, 'co_name', None) == 'inner':

        inner = type(outer)(f, globals())

        # can also use `types` module for readability:
        # inner = types.FunctionType(f, globals())

        print inner(42) # 52

L'idée est d'extraire l'objet de code de la fonction interne et de créer une nouvelle fonction à partir de celle-ci.

Un travail supplémentaire est requis lorsqu'une fonction interne peut contenir des variables libres. Vous devrez également les extraire et les transmettre au constructeur de la fonction dans le dernier argument (closure).

1
georg

Bien que ce soit la bête la plus laide jamais vue par l'humanité, voici comment vous pouvez le faire en utilisant une variable globale dans votre appel à la fonction exec:

def my_exec(code):
    exec('global i; i = %s' % code)
    global i
    return i

Cela utilise mal les variables globales pour faire passer vos données à la frontière.

>>> my_exec('1 + 2')
3

Inutile de dire que vous devriez jamais autoriser toute entrée d’utilisateur pour l’entrée de cette fonction, car cela pose un risque extrême pour la sécurité.

0
devsnd