Как выполнить строку, содержащую код 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
etc, которое они могут затем записать как 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
являются правильным решением, и они могут быть использованы более безопасным способом.
Как уже говорилось в справочном руководстве Пайтон и четко объяснено в этом учебнике, 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
.Тем не менее, первым шагом должно быть спросить себя, действительно ли вам это нужно. Выполнение кода, как правило, должно быть последним средством : он медленный, безобразный и опасный, если он может содержать введенный пользователем код. Вы всегда должны сначала смотреть на альтернативы, такие как функции высшего порядка , чтобы увидеть, могут ли они лучше удовлетворить ваши потребности.
установить и получить значения переменных с именами в строках
[while
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" будет выполнять apply_address_filter из файла customer_filters \ jj_filter.py