例如,如果我有一个add
名为
def add(x,y):
return x+y
我希望能够将字符串或输入转换为直接指向该函数,例如
w=raw_input('Please input the function you want to use')
或者
w='add'
有什么方法可以使用 w 来引用函数add
吗?
例如,如果我有一个add
名为
def add(x,y):
return x+y
我希望能够将字符串或输入转换为直接指向该函数,例如
w=raw_input('Please input the function you want to use')
或者
w='add'
有什么方法可以使用 w 来引用函数add
吗?
由于您正在接受用户输入,因此最安全的方法是准确定义什么是有效输入:
dispatcher={'add':add}
w='add'
try:
function=dispatcher[w]
except KeyError:
raise ValueError('invalid input')
如果要评估类似 的字符串'add(3,4)'
,可以使用安全 eval:
eval('add(3,4)',{'__builtins__':None},dispatcher)
eval
一般来说,当应用于用户输入时可能会很危险。上述内容更安全,因为__builtins__
已禁用并且locals
仅限于dispatcher
. 比我聪明的人也许还能制造麻烦,但我不能告诉你怎么做。
警告: Even应用于用户输入eval(..., {'__builtins__':None}, dispatcher)
是不安全的。如果有机会让他的字符串由eval
.
一种安全的方法是从名称映射到函数。它比使用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.'
内置功能eval
会做你想做的事。所有关于执行任意用户提供的代码的常见警告都适用。
如果有有限数量的预定义函数,您应该避免eval
使用查找表,而是使用查找表(即Dict
)。永远不要相信你的用户。
unutbu 的解决方案是我通常会使用的,但为了完整起见:
如果您要指定函数的确切名称,则可以使用eval
,尽管非常不鼓励这样做,因为人们可能会做恶意的事情:
eval("add")(x,y)
只需使用函数参考:
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')
简单而安全。
如果您正在实现一个类似 shell 的应用程序,其中用户输入一些命令(例如add),并且应用程序响应(返回总和),您可以使用该cmd
模块,它为您处理所有命令交互和调度。这是一个例子:
#!/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')
这是一个示例运行会话:
$ python cmd_tryout.py
输入有效命令列表的帮助
(Cmd) help add
add - 添加两个数字打印总和
(Cmd) add 5 3
8
(Cmd) quit
在提示符(Cmd)下,您可以发出help
免费获得的命令。其他命令是add
和quit
对应于do_add()
和do_quit()
功能。
请注意,帮助命令显示您的函数的文档字符串。文档字符串是紧跟在函数声明之后的字符串(参见do_add()
示例)。
该cmd
模块不做任何参数拆分,解析,所以你必须自己做。该do_add()
函数说明了这一点。
这个示例程序应该足以让您入门。有关更多信息,请查看cmd帮助页面。自定义程序的提示和其他方面是琐事。
我有同样的问题。
我建议您处理它的方式是创建一个临时 Python 文件来存储用户输入的函数。这是我在编写的用于绘制数学函数表示的程序中使用的示例:
with open("function.py",'w') as file:
f=input('enter the function you want to draw example: 2*x+1 or e**x :\n')
file.write("from math import *\ndef f(x):\n\treturn "+f)
这将创建一个包含我要调用的函数的文件。
接下来,您必须将您在文件中编写的函数调用到您的程序中:
from function import f
现在您可以将您的函数用作普通的 python 函数。
如果需要,您还可以使用 os.remove 删除存储函数的文件:
import os
os.remove("function.py")
为了帮助您理解,这是我绘制数学函数的程序:
import numpy
import cv2
import os
from math import *
def generate(f,a,b,min,max,functionname='noname'):
ph=(b-a)/1920
pv=(max-min)/1080
picture=numpy.zeros((1080,1920))
for i in range(0,1920):
picture[1079-(int((f(a+(i+1)*ph)*1080/max))),i]=255
for i in range(1920):
picture[1079-(int((f(a+(i+1)*ph)*1080/max)))+1,i]=255
cv2.imwrite(functionname+'.png',picture)
with open("function.py",'w') as file:
f=input('enter the function you want to draw example: or e**x :\n')
file.write("from math import *\ndef f(x):\n\treturn "+f)
from function import f
os.remove("function.py")
d=input('enter the interval ,min ,max and the image file name. Separate characters with spacebar. Example: 0 1 0 14 exponontielle :\n').split(" ")
generate(f,int(d[0]),int(d[1]),int(d[2]),int(d[3]),d[4])
我有很多情况需要在 Django 模板中将字符串与 int 进行比较,反之亦然。
我创建了一个过滤器,允许我传入函数名并使用 eval() 对其进行转换。
例子:
模板:
{% ifequal string int|convert:'str' %} do something {% endifequal %}
模板过滤器(我使用字符串来调用函数名):
@register.filter
def convert(value, funcname):
try:
converted = eval(funcname)(value)
return converted
except:
return value
def add(x,y):
print(x+y)
def subtract(x,y):
print(x-y)
function_list = {'add', 'subtract'}
def caller(func, x, y):
eval(func)(x,y) # more security exploits
if func in function_list:
eval(func)(x,y) # less security exploits
caller("add", 1, 2)
[我通过一个重复的问题来到这里。我的第一个想法是使用argparse
,shlex
但我在这里没有看到,所以我将其添加为另一个选项。]
您可以argparse
用来设置函数/命令的注册表并安全地解析它们的参数。这也将提供一定程度的用户友好性,例如,让您知道您何时输入了不存在的命令。
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()
参考上面 John Curry 的问题……如果你想要一个处理多个参数的 Jefferson Felix 的代码版本,那么最简单的解决方案是提供一个列表中的参数,并安排每个调度的函数来检查参数计数在继续之前。
我刚刚在 Visual Studio 代码中测试的一个简单版本如下:
import math
def sin(args):
argc = len(args)
if (argc == 1):
result = math.sin(args[0])
else:
result = None
return(result)
def sum(args):
argc = len(args)
if (argc == 2):
result = args[0] + args[1]
else:
result = None
return(result)
def dot_product(args):
argc = len(args)
if (argc == 2):
vector1 = args[0]
vector2 = args[1]
if (len(vector1) == 3 and len(vector2) == 3):
result = (vector1[0] * vector2[0]) + (vector1[1] * vector2[1]) + (vector1[2] * vector2[2])
else:
result = None
else:
result = None
return(result)
dispatcher = {"sin" : sin, "sum" : sum, "dot_product" : dot_product}
def call_func(dispatcher, func_name, args):
func_list = list(dispatcher.keys())
if (func_list.count(func_name) == 0):
return(None)
else:
return(dispatcher[func_name](args))
val = call_func(dispatcher, "sin", [0.6])
print(f"Sine is : {val}")
val = call_func(dispatcher, "sum", [4, 6])
print(f"sum is : {val}")
val = call_func(dispatcher, "dot_product", [[3, 7, 2], [5, 9, 4]])
print(f"dot product is : {val}")
输出如下所示:
Sine is : 0.5646424733950354
sum is : 10
dot product is : 86
当然,更复杂的版本将包括更好的错误捕获,如果发现错误,只需返回“None”,但以上可以用作构建模板。同样,可以改进 dot_product 函数以处理任何维度的向量,但我将其作为练习留给读者...