ユーザ入力から関数を呼び出したいが、引数を括弧で入れたい。たとえば、私は1つの引数取る関数がある場合:ユーザ入力に基づいて引数を持つPython関数を呼び出す
def var(value):
print(value)
私は、その後の引数で関数を呼び出すには、コマンドと引数をユーザーにお願いしたいと思います:
Input Command: var("Test")
Test
ユーザ入力から関数を呼び出したいが、引数を括弧で入れたい。たとえば、私は1つの引数取る関数がある場合:ユーザ入力に基づいて引数を持つPython関数を呼び出す
def var(value):
print(value)
私は、その後の引数で関数を呼び出すには、コマンドと引数をユーザーにお願いしたいと思います:
Input Command: var("Test")
Test
スプリット関数名を引数から削除します。あらかじめ定義されたマップを使用して関数を名前で検索します。引き数を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'を' dir() 'カテゴリのインデックスにしようとしました。理論的には。 – MasterHolbytla
@ MasterHolbytla私はそれが何を意味するのか分かりませんが、あなたは突然、 'eval'が安全でないことを修正しませんでした。私はそれを保証する。 – davidism
私は同意するでしょう。私は承認された機能だけを評価する方法に取り組んでいます。 – MasterHolbytla
私は次のような簡単なの入力を扱っているという仮定の下で、代替として、このソリューションを投稿するつもりです:
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
代わりに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()
これは、かなりラフな設定です。
デシリアライズ関数があまりにも多く見える場合は、デシリアライズをカスタム関数自体に移すこともできます。
cmdモジュールを調べる必要があります。これにより、シェルコマンドと同様の入力を解析することができますが、括弧が仕様の重要な部分である場合は、手間がかかり、区切り文字を変更できると思います。
コールと引数をキャプチャしてコールをディスパッチするパーサーを作成する必要があります。信用できない入力に対して 'eval'を使うべきではないからです。 – davidism
@davidism私が定義した関数に対してのみ 'eval'を使うことができますか? – MasterHolbytla
@MasterHolbytlaはあなたの入力です。常に 'var(arg1、arg2、arg3)'と同じくらいシンプルになりますか?それとももっと複雑になるのですか? – idjaw