Як виконати рядок, що містить код Python в Python?
Як виконати рядок, що містить код Python в Python?
Відповіді:
Для операторів використовуйте exec(string)
(Python 2/3) або exec string
(Python 2):
>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world
Коли вам потрібно значення виразу, використовуйте eval(string)
:
>>> x = eval("2+2")
>>> x
4
Однак перший крок повинен бути запитати себе, чи справді вам це потрібно. Виконання коду, як правило, займає крайню позицію: це повільно, негарно та небезпечно, якщо він може містити введений користувачем код. Ви завжди повинні спочатку переглянути альтернативи, такі як функції вищого порядку, щоб побачити, чи можуть вони краще відповідати вашим потребам.
if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42
тощо, про що вони можуть записати як exec ("x.%s = 42" % s)
. У цьому поширеному випадку (коли вам потрібно отримати доступ лише до атрибута об'єкта, який зберігається в рядку), є набагато швидша, чистіша та безпечніша функція getattr
: просто напишіть, getattr(x, s) = 42
щоб означати те саме.
setattr(x, s, 42)
? Я спробував, getattr(x, 2) = 42
і це не вдалосяcan't assign to function call: <string>, line 1
setattr(x, s, 42)
правильний синтаксис. Здивований, що пройшло стільки часу, щоб цю помилку виявити. У всякому разі, справа в тому , що getattr
і setattr
є альтернативою , exec
коли все , що ви хочете, щоб отримати довільний елемент, подивився на рядку.
У прикладі рядок виконується як код за допомогою функції exec.
import sys
import StringIO
# create file-like string to capture output
codeOut = StringIO.StringIO()
codeErr = StringIO.StringIO()
code = """
def f(x):
x = x + 1
return x
print 'This is my output.'
"""
# capture output and errors
sys.stdout = codeOut
sys.stderr = codeErr
exec code
# restore stdout and stderr
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
print f(4)
s = codeErr.getvalue()
print "error:\n%s\n" % s
s = codeOut.getvalue()
print "output:\n%s" % s
codeOut.close()
codeErr.close()
exec
(якщо ви не знаєте, що рядок коду надходить із надійного джерела).
eval
і exec
є правильним рішенням, і їх можна використовувати безпечніше .
Як обговорюється в довідковому посібнику Python і чітко пояснено в цьому підручнику, функції eval
та exec
функції приймають два додаткові параметри, що дозволяють користувачеві визначати, які глобальні та локальні функції та змінні доступні.
Наприклад:
public_variable = 10
private_variable = 2
def public_function():
return "public information"
def private_function():
return "super sensitive information"
# make a list of safe functions
safe_list = ['public_variable', 'public_function']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
# add any needed builtins back in
safe_dict['len'] = len
>>> eval("public_variable+2", {"__builtins__" : None }, safe_dict)
12
>>> eval("private_variable+2", {"__builtins__" : None }, safe_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'private_variable' is not defined
>>> exec("print \"'%s' has %i characters\" % (public_function(), len(public_function()))", {"__builtins__" : None}, safe_dict)
'public information' has 18 characters
>>> exec("print \"'%s' has %i characters\" % (private_function(), len(private_function()))", {"__builtins__" : None}, safe_dict)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'private_function' is not defined
По суті ви визначаєте простір імен, в якому буде виконуватися код.
eval
або exec
призначене для використання exec(input("Type what you want"))
? Є багато випадків, коли програма може записати процедуру або функцію в результаті обчислення; отримані функції будуть настільки ж безпечними та швидкими (колись оціненими), як і будь-яка інша частина хорошої та добре написаної програми. Небезпечна програма, що містить exec
не більш небезпечну, ніж небезпечна програма, яка завдає шкоди сама по собі, оскільки exec
не дає ніяких нових привілеїв програмі.
exec
іeval
exec
та використання eval
Python дуже нахмурене.З верхньої відповіді (наголос мій):
Для тверджень використовуйте
exec
.Коли вам потрібно значення виразу, використовуйте
eval
.Однак перший крок повинен бути запитати себе, чи справді вам це потрібно. Виконання коду, як правило, займає крайню позицію : це повільно, негарно та небезпечно, якщо він може містити введений користувачем код. Ви завжди повинні спочатку переглянути альтернативи, такі як функції вищого порядку , щоб побачити, чи можуть вони краще відповідати вашим потребам.
Від альтернативи до exec / eval?
встановити і отримати значення змінних з іменами в рядках
[поки
eval
] буде працювати, зазвичай не рекомендується використовувати імена змінних, що мають значення для самої програми.Натомість краще використовувати диктант.
Від http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (моє наголос)
Python - це не PHP
Не намагайтеся обійти ідіоми Python, оскільки інша мова робить це інакше. Простори імен є в Python не просто так, тому що він дає вам інструмент,
exec
це не означає, що ви повинні використовувати цей інструмент.
З http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (моє наголос)
Тож eval не є безпечним, навіть якщо ви видалите всі глобальні та вбудовані!
Проблема всіх цих спроб захисту eval () полягає в тому, що вони є чорними списками . Вони явно видаляють речі, які можуть бути небезпечними. Це програшний бій, тому що якщо у списку залишився лише один предмет, ви можете атакувати систему .
Тож чи можна зробити eval безпечним? Важко сказати. На даний момент я найкраще здогадуюсь, що ви не можете заподіяти ніякої шкоди, якщо не можете скористатися подвійними підкресленнями, тому, можливо, якщо ви виключаєте будь-яку рядок з подвійними підкресленнями, ви в безпеці. Можливо...
З http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (наголос мій):
По-перше,
exec
людям важче читати ваш код . Для того, щоб з'ясувати, що відбувається, мені не просто потрібно читати ваш код, я повинен прочитати ваш код, з’ясувати, яку строку він буде генерувати, а потім прочитати цей віртуальний код. Отже, якщо ви працюєте в команді або публікуєте програмне забезпечення з відкритим кодом або просите про допомогу десь на зразок StackOverflow, ви ускладнюєте допомогу іншим людям. І якщо є шанс, що ви будете налагоджувати або розширювати цей код через 6 місяців, ви ускладнюєте себе безпосередньо.
cfg.yaml
): reldir : ../my/dir/
і reldir = cfg[reldir]
. Однак, оскільки цей код python працюватиме як в Windows, так і в Linux, мені це потрібно, щоб підлаштовуватися під різні роздільники шляху операційних систем; або \\
або /
. Тому я використовую reldir : os.path.join('..','my','dir')
у конфігураційному файлі. Але це призводить до reldir
того, що ця буквальна рядок не оцінюється, тому я не можу відкрити файл у reldir
. У вас є пропозиція?
Як згадували інші, це "exec" ..
але, якщо ваш код містить змінні, ви можете використовувати "глобальний" для доступу до нього, також, щоб запобігти компілятору викликати таку помилку:
NameError: ім'я 'p_variable' не визначено
exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)
Варто зазначити, що exec
брат є так само закликаним, execfile
якщо ви хочете викликати файл python. Іноді це добре, якщо ви працюєте в сторонній пакет, який містить страшні IDE, і ви хочете кодувати поза їх пакетом.
Приклад:
execfile('/path/to/source.py)'
або:
exec(open("/path/to/source.py").read())
Я спробував досить багато речей, але єдине, що працювало, було наступне:
temp_dict = {}
exec("temp_dict['val'] = 10")
print(temp_dict['val'])
вихід:
10
Гаразд .. Я знаю, що це не зовсім відповідь, але, можливо, замітка для людей, які дивляться на це так, як я. Я хотів виконати конкретний код для різних користувачів / клієнтів, але також хотів уникнути exec / eval. Спочатку я намагався зберігати код у базі даних для кожного користувача і робив вище.
Я в кінцевому підсумку створив файли у файловій системі в папці 'customer_filters' і використовував модуль 'imp', якщо для цього клієнта не застосовувався фільтр, він просто продовжувався
import imp
def get_customer_module(customerName='default', name='filter'):
lm = None
try:
module_name = customerName+"_"+name;
m = imp.find_module(module_name, ['customer_filters'])
lm = imp.load_module(module_name, m[0], m[1], m[2])
except:
''
#ignore, if no module is found,
return lm
m = get_customer_module(customerName, "filter")
if m is not None:
m.apply_address_filter(myobj)
тому customerName = "jj" буде виконувати application_address_filter з файлу customer_filters \ jj_filter.py