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?
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
.
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.'
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)
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.
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.
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é.
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
[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()