web-dev-qa-db-fra.com

Comment convertir une chaîne en une fonction en python?

Par exemple, si j’ai une fonction appelée add like

def add(x,y):
    return x+y

et je veux la possibilité de convertir une chaîne ou une entrée pour diriger cette fonction comme

w=raw_input('Please input the function you want to use')

ou

w='add'

Est-il possible d'utiliser w pour faire référence à la fonction add? 

29
Bob

Puisque vous utilisez les entrées utilisateur, le moyen le plus sûr est de définir exactement ce qui est une entrée valide:

dispatcher={'add':add}
w='add'
try:
    function=dispatcher[w]
except KeyError:
    raise ValueError('invalid input')

Si vous souhaitez évaluer des chaînes telles que 'add(3,4)', vous pouvez utiliser safe eval :

eval('add(3,4)',{'__builtins__':None},dispatcher)

eval en général pourrait être dangereux lorsqu'il est appliqué à la saisie de l'utilisateur. Ce qui précède est plus sûr car __builtins__ est désactivé et locals est limité à dispatcher. Quelqu'un plus intelligent que moi pourrait peut-être encore causer des problèmes, mais je ne saurais vous dire comment le faire.

WARNING: Même eval(..., {'__builtins__':None}, dispatcher) est unsafe à appliquer à la saisie de l'utilisateur. Un utilisateur malveillant peut exécuter des fonctions arbitraires sur votre machine s’il est donné l’occasion de faire évaluer sa chaîne par eval

39
unutbu

Un moyen sûr consiste à mapper des noms vers des fonctions. C'est plus sûr que d'utiliser eval.

function_mappings = {
        'add': add,
}

def select_function():
    while True:
        try:
            return function_mappings[raw_input('Please input the function you want to use')]
        except KeyError:
            print 'Invalid function, try again.'
16
Chris Morgan

la solution de unutbu est celle que j'utiliserais normalement, mais pour être complet:

Si vous spécifiez le nom exact de la fonction, vous pouvez utiliser eval, bien que cela soit fortement déconseillé, car les utilisateurs peuvent commettre des actes malveillants:

eval("add")(x,y)
10
Foo Bah

La fonction intégrée eval fera ce que vous voulez. Tous les avertissements habituels concernant l'exécution de code arbitraire fourni par l'utilisateur s'appliquent.

S'il existe un nombre fini de fonctions prédéfinies, évitez eval et utilisez plutôt une table de correspondance (c'est-à-dire Dict). Ne faites jamais confiance à vos utilisateurs.

8
Oscar Korz

Si vous implémentez une application de type Shell dans laquelle l'utilisateur entre une commande (telle que add) et les réponses de l'application (renvoie la somme), vous pouvez utiliser le module cmd, qui gère toutes les interactions de commande et leur distribution. pour vous. Voici un exemple:

#!/usr/bin/env python

import cmd
import shlex
import sys

class MyCmd(cmd.Cmd):
    def do_add(self, arguments):
        '''add - Adds two numbers the print the sum'''
        x, y = shlex.split(arguments)
        x, y = int(x), int(y)
        print x + y

    def do_quit(self, s):
        '''quit - quit the program'''
        sys.exit(0)

if __== '__main__':
    cmd = MyCmd()
    cmd.cmdloop('type help for a list of valid commands')

Voici un exemple de session en cours:

$ python cmd_tryout.py
tapez help pour une liste de commandes valides
(Cmd) help add
add - Ajoute deux chiffres à l'impression de la somme
(Cmd) ajouter 5 3
8
(Cmd) quit 

À l'invite (Cmd)} _, vous pouvez émettre la commande help que vous recevez gratuitement. Les autres commandes sont add et quit qui correspondent aux fonctions do_add() et do_quit().

Notez que la commande help affiche la docstring de votre fonction. La docstring est une chaîne qui suit immédiatement la déclaration de la fonction (voir do_add() pour un exemple).

Le module cmd ne fait aucune division d'argument, analyse, donc vous devez le faire vous-même. La fonction do_add() illustre cela.

Cet exemple de programme devrait suffire à vous aider à démarrer. Pour plus d'informations, consultez la page cmd help. Il est facile de personnaliser l'invite et d'autres aspects de votre programme.

4
Hai Vu

Il suffit d'utiliser la référence de la fonction:

def pwr(x, y):
    return x ** y

def add(x, y):
    return x + y

dispatcher = { 'pwr' : pwr, 'add' : add}

def call_func(x, y, func):
    try:
        return dispatcher[func](x, y)
    except:
        return "Invalid function"

call_func(2, 3, 'add')

Simple et sécurisé.

2
Jefferson Felix

J'ai souvent eu à comparer une chaîne avec un int et inversement dans un modèle Django.

J'ai créé un filtre qui m'a permis de transmettre le nom de la fonction et de le convertir à l'aide de eval ().

Exemple:

Modèle:

{% ifequal string int|convert:'str' %} do something {% endifequal %}

Filtre de modèle (où j'utilise une chaîne pour appeler le nom de la fonction):

@register.filter
def convert(value, funcname):
    try:
        converted = eval(funcname)(value)
        return converted
    except:
        return value
2
sidarcy

[Je suis arrivé ici via une question en double. Ma première pensée a été d'utiliser argparse et shlex et je ne l'ai pas vu ici, donc je l'ajoute comme une autre option.]

Vous pouvez utiliser argparse pour configurer un registre de fonctions/commandes et analyser en toute sécurité leurs arguments. Cela offrira également un certain degré de convivialité, par exemple en vous permettant de savoir lorsque vous avez entré une commande inexistante.

import argparse
import shlex

def hello(name):
    print('hello,', name)

def main():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    hello_parser = subparsers.add_parser('hello')
    hello_parser.add_argument('name')
    hello_parser.set_defaults(func=hello)

    print('Enter q to quit')

    while True:
        command = input('command> ')
        command = command.strip()

        if not command:
            continue

        if command.lower() == 'q':
            break

        words = shlex.split(command)

        try:
            args = parser.parse_args(words)
        except SystemExit:
            # argparse will sys.exit() on -h and errors; prevent that
            continue

        func_args = {name: value for name, value in vars(args).items()}
        del func_args['func']
        args.func(**func_args)

if __== '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print()
0
user8651755