Як виконати рядок, що містить код Python в Python?


357

Як виконати рядок, що містить код Python в Python?


5
Справедливості, на відміну від іншого питання, це не є дублікатом. А інші розмістили набагато більше основних питань, ніж це.
DNS

8
Правильна відповідь, звичайно, майже завжди "не роби!".
bobince

23
@S. Лотт: Нещодавно не читав FAQ? "Також ідеально задавати і відповідати на власне питання програмування, але робити вигляд, що ви на небезпеці: сформулюйте це у формі запитання". Це не погане питання. +1
Devin Jeanpierre

49
@ S.Lott: Ні. Вам не потрібно. Якщо питання вже немає на сайті, то це чесна гра (відповідно до FAQ, як уже вказувалося). Просто відповідайте на кожне питання так, ніби ОП потребує допомоги. Вони можуть, ні, але наступний хлопець, який прочитає їх запитання, мабуть, буде. Всього мої 2 копійки.
Білл Ящірка

1
На все, що ви кажете не робити цього: у мене є випадок, коли мені це абсолютно потрібно. Він включає розподілену обробку.
судо

Відповіді:


331

Для операторів використовуйте 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

Однак перший крок повинен бути запитати себе, чи справді вам це потрібно. Виконання коду, як правило, займає крайню позицію: це повільно, негарно та небезпечно, якщо він може містити введений користувачем код. Ви завжди повинні спочатку переглянути альтернативи, такі як функції вищого порядку, щоб побачити, чи можуть вони краще відповідати вашим потребам.


1
а як щодо сфери дії коду, виконаного 'exec'? це вкладено?
jondinham

21
Поширений випадок, коли хтось хоче використовувати "exec" - це щось на зразок if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42тощо, про що вони можуть записати як exec ("x.%s = 42" % s). У цьому поширеному випадку (коли вам потрібно отримати доступ лише до атрибута об'єкта, який зберігається в рядку), є набагато швидша, чистіша та безпечніша функція getattr: просто напишіть, getattr(x, s) = 42щоб означати те саме.
ShreevatsaR

5
Як exec повільніше, ніж інтерпретатор пітона?
Cris Stringfellow

6
@ShreevatsaR Ви не маєте на увазі setattr(x, s, 42)? Я спробував, getattr(x, 2) = 42і це не вдалосяcan't assign to function call: <string>, line 1
Таннер Семерад

6
@Tanner: Хм. Так, справжній setattr(x, s, 42)правильний синтаксис. Здивований, що пройшло стільки часу, щоб цю помилку виявити. У всякому разі, справа в тому , що getattrі setattrє альтернативою , execколи все , що ви хочете, щоб отримати довільний елемент, подивився на рядку.
ShreevatsaR

68

У прикладі рядок виконується як код за допомогою функції 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()

1
витісняючи stdout та stderr подібними, це робить мене дуже нервовим. схоже, це може спричинити величезні проблеми із безпекою. чи є шлях до цього?
Нарколапсер

1
@Narcolapser вам слід взагалі більше займатися використанням exec(якщо ви не знаєте, що рядок коду надходить із надійного джерела).
bruno desthuilliers

27

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

По суті ви визначаєте простір імен, в якому буде виконуватися код.


9
Зробити безпеку eval неможливо: Eval справді небезпечний . Якщо ви берете у мене код і оцінюєте його, я можу сегментувати вашу програму Python. Гра завершена.
Нед Батчелдер

3
@ v.oddou Я відповідав на заяву Алана, "eval і exec .. можна використовувати безпечно". Це помилково. Якщо хтось сказав: "Баш можна використовувати безпечно", це також було б помилковим. Баш небезпечний. Це необхідна небезпека, але все ж небезпечна. Стверджувати, що eval можна зробити безпечним, просто неправильно.
Нед Батчелдер

1
@NedBatchelder так. і посилання, на яке ви вказуєте, є хорошим матеріалом. з владою приходить відповідальність, тому справа просто в тому, щоб усвідомлювати потенційну силу евалу. і якщо ми вирішимо, що сила = небезпека.
v.oddou

3
@ NedBatchelder Багато фрагментів коду, написаних на Python, також можуть бути небезпечними, але чому ви припускаєте, що це evalабо execпризначене для використання exec(input("Type what you want"))? Є багато випадків, коли програма може записати процедуру або функцію в результаті обчислення; отримані функції будуть настільки ж безпечними та швидкими (колись оціненими), як і будь-яка інша частина хорошої та добре написаної програми. Небезпечна програма, що містить execне більш небезпечну, ніж небезпечна програма, яка завдає шкоди сама по собі, оскільки execне дає ніяких нових привілеїв програмі.
Томас Баручель

1
@ThomasBaruchel знову, мій погляд, - протиставитись думці про те, що eval або exec можна зробити безпечними. Зокрема, ця відповідь стверджує, що контроль над глобальними та місцевими жителями дозволить безпечно їх використовувати. Це помилково. Кожен раз, коли ви використовуєте exec та eval, ви повинні точно знати, який код виконується. Якщо ви цього не зробите, то ви відкриті для небезпечних операцій.
Нед Батчелдер

21

Пам'ятайте, що з версії 3 execце функція!
тому завжди використовуйте exec(mystring)замість exec mystring.


11

eval()є лише для виразів, хоча eval('x+1')працює, наприклад eval('x=1'), не працюватиме. У такому випадку краще скористатися exec, а ще краще: спробуйте знайти краще рішення :)


11

Уникайте execіeval

Використання execта використання evalPython дуже нахмурене.

Є кращі альтернативи

З верхньої відповіді (наголос мій):

Для тверджень використовуйте 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 місяців, ви ускладнюєте себе безпосередньо.


"Я найкраще здогадуюсь, що ви не можете заподіяти ніякої шкоди, якщо не можете скористатися подвійними підкресленнями" - Ви можете побудувати рядок, що містить подвійні підкреслення, а потім викликати eval цю рядок.
Стенлі Бек

хороша порада, якщо ви не пишете генератор коду, чи виконуєте роботу або подібне ... чим, мабуть, займаються більшість людей тут.
Ерік Аронесті

Мені потрібно імпортувати відносний шлях з config-файла ( cfg.yaml): reldir : ../my/dir/ і reldir = cfg[reldir]. Однак, оскільки цей код python працюватиме як в Windows, так і в Linux, мені це потрібно, щоб підлаштовуватися під різні роздільники шляху операційних систем; або \\ або /. Тому я використовую reldir : os.path.join('..','my','dir')у конфігураційному файлі. Але це призводить до reldirтого, що ця буквальна рядок не оцінюється, тому я не можу відкрити файл у reldir. У вас є пропозиція?
Марі. П.

9

Ви виконуєте код виконання за допомогою exec, як у наступному сеансі IDLE:

>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']

4

1
Це не працює в нормальному пітоні. Принаймні, не в
пітоні

6

Як згадували інші, це "exec" ..

але, якщо ваш код містить змінні, ви можете використовувати "глобальний" для доступу до нього, також, щоб запобігти компілятору викликати таку помилку:

NameError: ім'я 'p_variable' не визначено

exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)


4

Варто зазначити, що execбрат є так само закликаним, execfileякщо ви хочете викликати файл python. Іноді це добре, якщо ви працюєте в сторонній пакет, який містить страшні IDE, і ви хочете кодувати поза їх пакетом.

Приклад:

execfile('/path/to/source.py)'

або:

exec(open("/path/to/source.py").read())



1

Я спробував досить багато речей, але єдине, що працювало, було наступне:

temp_dict = {}
exec("temp_dict['val'] = 10") 
print(temp_dict['val'])

вихід:

10


0

Найбільш логічним рішенням буде використання вбудованої функції eval (). Іншим рішенням є записати цю рядок у тимчасовий файл python та виконати її.


0

Гаразд .. Я знаю, що це не зовсім відповідь, але, можливо, замітка для людей, які дивляться на це так, як я. Я хотів виконати конкретний код для різних користувачів / клієнтів, але також хотів уникнути 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


1
Як ви керували безпекою? Як ви знаєте, що клієнти не зловживають цією пільгою?
JSBach
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.