2016-10-19 12 views
-5

ユーザ入力から関数を呼び出したいが、引数を括弧で入れたい。たとえば、私は1つの引数取る関数がある場合:ユーザ入力に基づいて引数を持つPython関数を呼び出す

def var(value): 
    print(value) 

私は、その後の引数で関数を呼び出すには、コマンドと引数をユーザーにお願いしたいと思います:

Input Command: var("Test") 
Test 
+6

コールと引数をキャプチャしてコールをディスパッチするパーサーを作成する必要があります。信用できない入力に対して 'eval'を使うべきではないからです。 – davidism

+0

@davidism私が定義した関数に対してのみ 'eval'を使うことができますか? – MasterHolbytla

+0

@MasterHolbytlaはあなたの入力です。常に 'var(arg1、arg2、arg3)'と同じくらいシンプルになりますか?それとももっと複雑になるのですか? – idjaw

答えて

9

スプリット関数名を引数から削除します。あらかじめ定義されたマップを使用して関数を名前で検索します。引き数を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.あなたが期待している素敵な入力を評価しますが、たとえば、すべてのファイルを削除するコードも評価されます。

+0

私は 'eval'を' dir() 'カテゴリのインデックスにしようとしました。理論的には。 – MasterHolbytla

+5

@ MasterHolbytla私はそれが何を意味するのか分かりませんが、あなたは突然、 'eval'が安全でないことを修正しませんでした。私はそれを保証する。 – davidism

+0

私は同意するでしょう。私は承認された機能だけを評価する方法に取り組んでいます。 – MasterHolbytla

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 
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() 

これは、かなりラフな設定です。

デシリアライズ関数があまりにも多く見える場合は、デシリアライズをカスタム関数自体に移すこともできます。

2

cmdモジュールを調べる必要があります。これにより、シェルコマンドと同様の入力を解析することができますが、括弧が仕様の重要な部分である場合は、手間がかかり、区切り文字を変更できると思います。

関連する問題