Изпълнение на код на 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

Кой е най-добрият начин да направите това?


person Brad Zeis    schedule 18.06.2009    source източник


Отговори (4)


Можете да използвате метода 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. Той комбинира този речник със списъка на текущите глобални в изпълняващия модул.

НЕУСПЕХ

Оказва се (благодаря на коментаторите!), че всъщност трябва да използвате оператора exec. Голямо опа. Ревизираните примери са както следва:


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

(Това изглежда познато!) Exec е израз:
exec "code" [in scope] Където 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")
person Lucas Jones    schedule 18.06.2009
comment
Имайте предвид, че това ще работи само в python3. В python2.x print е оператор, а не функция, така че това дава синтактична грешка. Вместо това ще трябва да използвате exec. - person Brian; 19.06.2009
comment
Да, ако той използва pygame, той използва python2.x, тъй като не вярвам, че имат версия на python3. - person James McMahon; 19.06.2009
comment
Функцията eval() поддържа само оценяване на изрази на Python, а не произволни изрази. Изявлението exec трябва да се използва за изрази. - person Greg Hewgill; 19.06.2009
comment
Току-що тествах това интерактивно и да, Грег е прав (както се очаква!). Промених отговора си и запазих старите неща там за бъдещето. Включих също не относно изявлението за печат. - person Lucas Jones; 19.06.2009

Както посочиха други, можете да заредите текста в низ и да използвате exec "codestring". Ако вече се съдържа във файл, използването на execfile ще избегне необходимостта да го зареждате.

Една бележка за ефективността: Трябва да избягвате да изпълнявате кода многократно, тъй като анализирането и компилирането на източника на Python е бавен процес. т.е. нямам:

def keydown(self, key):
    exec user_code

Можете да подобрите това малко, като компилирате изходния код в кодов обект (с compile() и exec, или по-добре, като конструирате функция, която запазвате и изграждате само веднъж. Или изисквайте от потребителя да напише „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)
person Brian    schedule 18.06.2009

Можете да използвате eval()

person anthony    schedule 18.06.2009

eval или exec. Определено трябва да прочетете препратката към библиотеката на Python преди програмиране.

person stepancheg    schedule 18.06.2009