Выполнение кода Python, содержащегося в строке - PullRequest
8 голосов
/ 19 июня 2009

Я пишу игровой движок, используя 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

Какой лучший способ сделать это?

Ответы [ 4 ]

25 голосов
/ 19 июня 2009

Вы можете использовать метод 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:

  1. Дает текущему модулю другое имя (оно становится contrivedExample сценарию). Потребитель может звонить contrivedExample.hello() сейчас.)
  2. Он определяет hi как указывающий на hello
  3. Он объединил этот словарь со списком текущих глобальных переменных в исполняющем модуле.

FAIL

Оказывается (спасибо, комментаторы!), Что вам действительно нужно использовать оператор exec. Большой ой. Пересмотренные примеры:


exec Определение

(это выглядит знакомо!) Exec это утверждение:
exec "code" [in 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 - это утверждение во всех версиях Python до 3.0 В 2.6 поведение можно изменить, набрав from __future__ import print_statement. В противном случае используйте:

print "hello"

Вместо:

print("hello")
2 голосов
/ 19 июня 2009

Как уже отмечали другие, вы можете загрузить текст в строку и использовать exec "codestring". Если он уже содержится в файле, использование execfile позволит избежать его загрузки.

Одно замечание по производительности: вам следует избегать многократного выполнения кода, поскольку синтаксический анализ и компиляция исходного кода 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)
0 голосов
/ 19 июня 2009

Eval или Exec. Перед программированием обязательно прочитайте справочник по библиотеке Python.

0 голосов
/ 19 июня 2009

Вы можете использовать eval()

...