我想从用户输入调用函数,但在括号中包含参数。例如,如果我有一个带有一个参数的函数:
def var(value):
print(value)
我想问用户一个命令和参数,然后使用参数调用该函数:
Input Command: var("Test")
Test
答案 0 :(得分:8)
从参数中拆分函数名称。使用预定义的地图按名称查找功能。使用literal_eval
解析参数。使用参数调用函数。
available = {}
def register_func(f):
available[f.__name__] = f
@register_func
def var(value):
print(value)
from ast import literal_eval
def do_user_func(user_input):
name, args = user_input.split('(', 1)
return available[name](*literal_eval('(' + args[:-1] + ',)'))
do_user_func("var('test')") # prints "test"
这仍然非常脆弱,任何无效输入都将失败(例如忘记括号或无效的函数名称)。这取决于你使这个更强大。
literal_eval
在不受信任的输入上仍然有些不安全,因为可以构造评估为大量内存的小字符串。 '[' * 10 + ']' * 10
,是一个安全但具有示范性的例子。
最后,不要在不受信任的用户输入上使用eval
。 There is no practical way to secure it from malicious input.虽然它会评估您期望的好输入,但它也会评估代码,例如,会删除所有文件。
任何使eval
安全的尝试最终都会比这里的任何解决方案更复杂,没有任何实际好处。它在某种程度上仍然不会安全,你没有预料到。不要这样做。
答案 1 :(得分:3)
我打算将此解决方案作为替代方案发布,假设您正在处理简单输入,例如:
var(arg)
或者,单个函数调用可以获取位置参数列表。
通过使用eval
,如前所述,这将是一个可怕的未经推荐的想法。我认为这是您正在阅读的安全风险。
执行此方法的理想方法是使用字典,将字符串映射到要执行的方法。
此外,您可以考虑另一种方法来执行此操作。有一个空格分隔的输入,知道如何用参数调用你的函数。考虑这样的输入:
"var arg1 arg2"
所以当你输入:
call = input().split()
您现在将拥有:
['var', 'arg1', 'arg2']
您现在可以考虑函数的第一个参数,以及您传递给函数的参数的所有其他内容。所以,作为一个功能性的例子:
def var(some_arg, other_arg):
print(some_arg)
print(other_arg)
d = {"var": var}
call = input().split()
d[call[0]](*call[1:])
演示:
var foo bar
foo
bar
答案 2 :(得分:2)
您应该调查cmd模块。这允许您解析类似于shell命令的输入,但我相信如果括号是规范的重要部分,您可能会变得棘手并更改分隔符。
答案 3 :(得分:0)
您可以自己解析,而不是使用eval。这样,您就可以控制每个函数应如何解析/反序列化用户输入的参数。
import sys, re
def custom_print(value):
print value
def custom_add(addends):
print sum(addends)
def deserialize_print(args):
# just print it as is
custom_print(args)
def deserialize_add(args):
# remove all whitespace, split on commas, parse as floats
addends = [float(x) for x in re.sub(r"\s", "", args).split(",")]
# send to custom_add function
custom_add(addends)
def get_command():
cmd_input = raw_input("Command: ")
# -- check that the command is formatted properly
# and capture command groups
match = re.match(r"^([a-zA-Z0-9]+)(\(.*\))?$", cmd_input)
if match:
# extract matched groups to separate variables
(cmd, argstring) = match.groups()
# strip parenthesis off of argstring
if argstring:
args = argstring[1:-1]
# send the whole argument string to its corresponding function
if cmd == "print":
deserialize_print(args)
elif cmd == "add":
deserialize_add(args)
elif cmd == "exit":
sys.exit()
else:
print "Command doesn't exist."
else:
print "Invalid command."
# recurse until exit
get_command()
# -- begin fetching commands
get_command()
这是一个相当粗略的设置,尽管您可以通过更多错误检查和改进反序列化功能以及模块化功能添加来实现。
如果解耦的反序列化函数看起来太多,您也可以将反序列化移动到自定义函数中。
答案 4 :(得分:0)
以下是使用类从用户输入调用的函数示例:
class Wash:
def __init__(self, amount):
self.amount = amount
if amount == 12:
print("Platinum Wash")
elif amount == 6:
print("Basic Wash")
else:
print("Sorry!")
amount = int(input("Enter amount: "))
payment = Wash(amount)