可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
For example, if I have a function called add like
def add(x,y):
return x+y
and I want the ability to convert a string or an input to direct to that function like
w=raw_input('Please input the function you want to use')
or
w='add'
Is there any way to use w to refer to the function add?
回答1:
Since you are taking user input, the safest way is to define exactly what is valid input:
dispatcher={'add':add}
w='add'
try:
function=dispatcher[w]
except KeyError:
raise ValueError('invalid input')
If you want to evaluate strings like 'add(3,4)'
, you could use safe eval:
eval('add(3,4)',{'__builtins__':None},dispatcher)
eval
in general could be dangerous when applied to user input. The above is safer since __builtins__
is disabled and locals
is restricted to dispatcher
. Someone cleverer than I might be able to still cause trouble, but I couldn't tell you how to do it.
WARNING: Even eval(..., {'__builtins__':None}, dispatcher)
is unsafe to be applied to user input. A malicious user could run arbitrary functions on your machine if given the opportunity to have his string evaluated by eval
.
回答2:
One safe way is to map from names to functions. It's safer than using 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.'
回答3:
unutbu's solution is what I would normally use, but for completeness sake:
If you are specifying the exact name of the function, you can use eval
, although it is highly discouraged because people can do malicious things:
eval("add")(x,y)
回答4:
The built-in function eval
will do what you want. All the usual warnings about executing arbitrary user-supplied code apply.
If there are a finite number of predefined functions, you should avoid eval
and use a lookup table instead (i.e. Dict
). Never trust your users.
回答5:
If you are implementing a shell-like application where the user enter some command (such as add), and the application responses (return the sum), you can use the cmd
module, which handles all the command interactions and dispatching for you. Here is an example:
#!/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')
Here is a sample running session:
$ python cmd_tryout.py
type help for a list of valid commands
(Cmd) help add
add - Adds two numbers the print the sum
(Cmd) add 5 3
8
(Cmd) quit
At the prompt (Cmd), you can issue the help
command which you get for free. Other commands are add
and quit
which correspond to the do_add()
and do_quit()
functions.
Note that help command displays the docstring for your function. The docstring is a string immediately follows the function declararation (see do_add()
for example).
The cmd
module does not do any argument spliting, parsing, so you have to do it yourself. The do_add()
function illustrates this.
This sample program should be enough to get you started. For more information look up the cmd help page. It is trivia to customize the prompt and other aspect of your program.
回答6:
I've had many situation where I've needed to compare a string to an int and vice versa within a Django template.
I created a filter that allowed me to pass in the function name and using eval() convert it.
Example:
Template:
{% ifequal string int|convert:'str' %} do something {% endifequal %}
Template Filter (where i use a string to call the function name):
@register.filter
def convert(value, funcname):
try:
converted = eval(funcname)(value)
return converted
except:
return value
回答7:
Just use function reference:
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 and secure.
回答8:
[I got here via a duplicate question. My first thought was to use argparse
and shlex
and I didn't see that here, so I'm adding it as another option.]
You could use argparse
to set up a registry of functions/commands and safely parse their args. This will provide some level of user-friendliness too by, e.g., letting you know when you've entered a command that doesn't exist.
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()