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

Par exemple, si j'ai une fonction appelée ajouter comme

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

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

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

Ou

w='add'

Est-il un moyen d'utiliser w pour désigner la fonction ajouter?

25
demandé sur dreftymac 2011-10-11 02:37:32

8 réponses

Puisque vous prenez une entrée 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 les cordes, comme 'add(3,4)', vous pouvez utiliser sûr eval:

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

eval en général pourrait être dangereux lorsqu'il est appliqué à une entrée d'utilisateur. ce qui précède est plus sûr puisque __builtins__ est désactivé et locals est limité à dispatcher. Quelqu'un de plus intelligent que moi pourrait encore causer des problèmes, mais je ne pouvais pas vous dire comment faire il.

AVERTISSEMENT: Meme eval(..., {'__builtins__':None}, dispatcher) est dangereux pour être appliqué à une entrée d'utilisateur. Un utilisateur malveillant peut exécuter des fonctions arbitraires sur votre machine s'il a la possibilité d'avoir sa chaîne évaluée par eval.

37
répondu unutbu 2013-08-18 00:13:55

Un moyen sûr est de mapper des noms aux 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.'
12
répondu Chris Morgan 2011-10-10 22:43:11

La solution D'Unutbu est ce que j'utiliserais normalement, mais par souci d'exhaustivité:

Si vous spécifiez le nom exact de la fonction, vous pouvez utiliser eval, bien qu'il soit fortement déconseillé car les gens peuvent faire des choses malveillantes:

eval("add")(x,y)
8
répondu Foo Bah 2011-10-10 22:44:04

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

S'il y a un nombre fini de fonctions prédéfinies, vous devriez éviter eval et utiliser une table de recherche à la place (c'est-à-dire Dict). Ne faites jamais confiance à vos utilisateurs.

7
répondu Oscar Korz 2011-10-10 22:43:02

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 la répartition 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 __name__ == '__main__':
    cmd = MyCmd()
    cmd.cmdloop('type help for a list of valid commands')

Voici un exemple de session en cours d'exécution:

$ python cmd_tryout.py
tapez aide pour une liste de commandes valides
(Cmd) aide à ajouter
ajouter-ajoute deux numéros l'impression de la somme
(Cmd) ajouter 5 3
8
(Cmd) quitter

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

Notez que la commande help affiche la docstring de votre fonction. Le docstring est une chaîne qui suit immédiatement la déclaration de la fonction (Voir do_add() par exemple).

Le module cmd ne fait aucun argument spliting, l'analyse, donc vous devez le faire vous-même. La fonction do_add() l'illustre.

Cet exemple de programme devrait être suffisant pour vous aider à démarrer. Pour plus d'informations, consultez la page d'aide cmd. Il est trivia pour personnaliser l'invite et d'autres aspects de votre programme.

2
répondu Hai Vu 2013-04-30 15:49:34

J'ai eu beaucoup de situations où j'ai eu besoin de comparer une chaîne à un int et vice versa dans un modèle Django.

J'ai créé un filtre qui m'a permis de passer le nom de la fonction et d'utiliser eval () le convertir.

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
1
répondu sidarcy 2013-04-30 15:04:42

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é.

1
répondu Jefferson Felix 2018-07-21 12:40:24

[ je suis arrivé ici via une question en double. Ma première pensée était d'utiliser argparse et {[2] } 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 fournira également un certain niveau de convivialité, par exemple en vous permettant de savoir quand vous avez entré une commande qui n'existe pas.

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 __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print()
0
répondu 2017-10-29 00:46:30