文字列に含まれるPythonコードを実行する
質問
pygameとbox2dを使用してゲームエンジンを記述しています。キャラクタービルダーでは、キーダウンイベントで実行されるコードを記述できるようにします。
次のようなコードを記述できるテキストエディターをキャラクタービルダーに含めるつもりでした。
if key == K_a:
## Move left
pass
elif key == K_d:
## Move right
pass
テキストエディターの内容を文字列として取得し、このCharacterのメソッドのメソッドでコードを実行したい:
def keydown(self, key):
## Run code from text editor
それを行う最良の方法は何ですか?
解決
eval(string)
これを行うメソッド。
定義
eval(code、globals = None、locals = None)
コードは単なる標準のPythonコードです。つまり、適切にインデントする必要があります。
グローバルには、カスタムの __ builtins __
を定義できます。これは、セキュリティの目的に役立ちます。
例
eval("print('Hello')")
hello
をコンソールに出力します。使用するコードのローカル変数とグローバル変数を指定することもできます。
eval("print('Hello, %s'%name)", {}, {'name':'person-b'})
セキュリティの問題
ただし、注意してください。ユーザー入力が実行されます。考慮:
eval("import os;os.system('sudo rm -rf /')")
それを回避する方法はいくつかあります。最も簡単なのは次のようなことです:
eval("import os;...", {'os':None})
ハードドライブを消去するのではなく、例外をスローします。あなたのプログラムはデスクトップですが、人々がスクリプトを再配布した場合、これは問題になる可能性があります。
奇妙な例
これは eval
をかなり奇妙に使用する例です。
def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'
eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))
これがeval行で行うこと:
- 現在のモジュールに別の名前を付けます(スクリプトの
contrivedExample
になります)。コンシューマはcontrivedExample.hello()
をすぐに呼び出すことができます。) -
hi
はhello
を指していると定義します
- その辞書を、実行中のモジュールの現在のグローバルのリストと組み合わせました。
FAIL
exec
ステートメントを実際に使用する必要があることがわかりました(コメントありがとう!)。おっと。改訂された例は次のとおりです。
exec
定義
(これはおなじみですね!)
Execはステートメントです:
exec" code" [範囲内]
scopeは、ローカル変数とグローバル変数の両方の辞書です。これが指定されていない場合、現在のスコープで実行されます。
コードは単なる標準Pythonコードです。つまり、適切にインデントする必要があります。
exec
の例
exec "print('hello')"
hello
をコンソールに出力します。使用するコードのローカル変数とグローバル変数を指定することもできます。
eval "print('hello, '+name)" in {'name':'person-b'}
exec
セキュリティ上の問題
ただし、注意してください。ユーザー入力が実行されます。考慮:
exec "import os;os.system('sudo rm -rf /')"
ステートメントの印刷
コメンターも指摘しているように、 print
は3.0より前のすべてのバージョンのPythonのステートメントです。 2.6では、__ future__ import print_statement からを入力して動作を変更できます。それ以外の場合は、次を使用します。
print "hello"
の代わりに:
print("hello")
他のヒント
他の人が指摘したように、テキストを文字列にロードして、 exec" codestring"
を使用できます。既にファイルに含まれている場合、 execfile を使用すると、ファイルをロードする必要がなくなります。
1つのパフォーマンスノート:Pythonソースの解析とコンパイルは時間がかかるため、コードを複数回実行することは避けてください。すなわち。持っていない:
def keydown(self, key):
exec user_code
ソースをコードオブジェクトにコンパイルして( compile()
で実行するか、それ以上に、保持する関数を作成し、一度だけビルドすることで、これを少し改善できます。ユーザーに" def my_handler(args ...)"を記述することを要求するか、自分で先頭に追加して次のようにします:
user_source = "def user_func(args):\n" + '\n'.join(" "+line for line in user_source.splitlines())
d={}
exec user_source in d
user_func = d['user_func']
その後:
if key == K_a:
user_func(args)
eval()
を使用できます。
evalまたはexec。プログラミングする前に、Pythonライブラリリファレンスを必ずお読みください。