これを行う方法を示す非常に限定的な例を次に示します -- もちろん、ここにはいくつかの制限があります -- 主に、これはユーザーが 1 つの関数のみを入力した場合にのみ機能します。彼らが書いた文字列がより似ている場合:
a='garbage'
def foo():pass
あるいは:
def bar():
return foobar()
def foobar():
return "foobar is a cool word, don't you think?"
その後、あなたは運が悪いです。(言い換えると、これは、ユーザーが run_user 関数の名前空間に 1 つだけ追加していることを前提としています)。もちろん、それを確認して、ユーザーが追加しすぎたことが判明した場合は、例外などを発生させることができます...関数を返して、gauden の提案に従って使用することもできます。
def run_user(S):
#S is the user's function as a string.
lvars=None #make sure the name is in locals()
lvars=set(locals())
exec(S) #exec isn't usually a good idea -- but I guess you're a very trusting person.
usr_namespace=list(set(locals())-lvars)
usr_func_name=usr_namespace[0]
if(len(usr_namespace)>1):
raise ValueError("User input too much into the namespace!")
usr_func=locals()[usr_func_name]
usr_func() #comment this out if you don't want to run the function immediately
return usr_func
usr_string="""
def foo():
a="Blah"
print "Hello World! "+a
"""
func_handle=run_user(usr_string) #prints "Hello World! Blah"
#and to demonstrate that we can pass a handle to the function around:...
func_handle() #prints "Hello World! Blah" again.
exec
python 3または python 2を使用すると、これをもう少し安全に行うことができることに注意してください。辞書をグローバル辞書としてexecfile
渡すことで、ユーザーの関数の名前空間を制限できます。{'__builtins__':None}
#python3.x
allowed=vars(__builtins__).copy()
allowed['__import__']=None
exec("import os",{'__builtins__':None},allowed) #raises ImportError
exec("print(abs(-4))",{'__builtins__':None},allowed) #prints 4 as you'd expect.
execfile
文字列を一時ファイルに書き込んだ場合、python2.xでも同じことが機能すると思います...
編集(以下のコメントに対処するため)
あなたが提供する例はeval
、もう少し簡単に行うことができます:
a=5
b=eval('a+5') #b == 10
ただし、これはあなたが求めたものではありません。あなたが求めたのは、ユーザーが関数を記述できることでした。たとえば、次のようになります。
def f(a):
return a+5
前者の場合も機能しますが、ユーザーは変数名が「a」であることを知っている必要があります。
a=5
b=eval('x+5') #won't work -- x isn't defined
彼らはまた、ベクトルを追加する方法を知る必要があります--(あなたがnumpy配列を使用している場合、それは些細なことですが、そうでない場合に備えて言及すると思いました)。また、複雑な式 (複数の条件、ループなどを使用する長い式) を作成するには、かなりの量の作業と頭を悩ませる必要があります。
後者のケースは、はるかに一般的であるため、(私の意見では) 少し優れています。私が説明した方法を使用して関数を取得でき(実際に関数を実行する部分を削除します)、ユーザーは必要な変数名を使用できます-その後、関数を使用するだけです。また、ループなどを実行したり、eval
. これに対して支払う唯一のことは、ユーザーが書く必要があることdef func(...):
とreturn some_value
、その最後に、python が完全に直感的であることを知っている場合です。
ss="""
def foo(x):
return 5+x
"""
a=5
func=run_user(ss)
result=func(a) #result = 10
これには、関数を呼び出すたびに文字列を再解析する必要がないという利点もあります。を取得したらfunc
、好きなときにいつでも使用できます。また、私のソリューションでは、ユーザーが定義した関数の名前を知る必要さえないことに注意してください。関数オブジェクトを取得したら、名前は関係ありません。