Python Language
Dynamiczne wykonywanie kodu za pomocą programów „exec” i „eval”
Szukaj…
Składnia
- eval (wyrażenie [, globals = None [, locals = None]])
- exec (obiekt)
- exec (obiekt, globale)
- exec (obiekt, globale, lokalni)
Parametry
Argument | Detale |
---|---|
expression | Kod wyrażenia jako ciąg znaków lub obiekt code |
object | Kod instrukcji jako ciąg znaków lub obiekt code |
globals | Słownik używany dla zmiennych globalnych. Jeśli nie określono miejscowych, jest to również używane w przypadku miejscowych. Jeśli pominięto, używane są globals() zakresu wywoływania. |
locals | Obiekt odwzorowania używany dla zmiennych lokalnych. Jeśli zostanie pominięty, używany jest ten przekazany dla globals . Jeśli oba zostaną pominięte, wówczas globals() i locals() zakresu wywoływania są używane odpowiednio dla globals i locals . |
Uwagi
W exec
, jeśli globals
są locals
(tj. Odnoszą się do tego samego obiektu), kod jest wykonywany tak, jakby znajdował się na poziomie modułu. Jeśli globals
i locals
są odrębnymi obiektami, kod jest wykonywany tak, jakby był w treści klasy .
Jeśli obiekt globals
jest przekazywany, ale nie określa klucza __builtins__
, wówczas wbudowane funkcje i nazwy Pythona są automatycznie dodawane do zakresu globalnego. Aby stłumić dostępności funkcji takich jak print
lub isinstance
w zakresie wykonywanego niech globals
mają kluczową __builtins__
odwzorowaną na wartość None
. Nie jest to jednak funkcja bezpieczeństwa.
Nie należy używać składni specyficznej dla języka Python 2; składnia Python 3 będzie działać w Pythonie 2. Dlatego następujące formy są nieaktualne: <s>
-
exec object
-
exec object in globals
-
exec object in globals, locals
Ocena instrukcji za pomocą exec
>>> code = """for i in range(5):\n print('Hello world!')"""
>>> exec(code)
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Ocena wyrażenia za pomocą eval
>>> expression = '5 + 3 * a'
>>> a = 5
>>> result = eval(expression)
>>> result
20
Wstępna kompilacja wyrażenia w celu oceny go wiele razy
wbudowanej funkcji compile
można użyć do wstępnej kompilacji wyrażenia do obiektu kodu; ten obiekt kodu można następnie przekazać do eval. Przyspieszy to wielokrotne wykonywanie analizowanego kodu. Trzecim parametrem do compile
musi być ciąg 'eval'
.
>>> code = compile('a * b + c', '<string>', 'eval')
>>> code
<code object <module> at 0x7f0e51a58830, file "<string>", line 1>
>>> a, b, c = 1, 2, 3
>>> eval(code)
5
Ocena wyrażenia za pomocą eval przy użyciu niestandardowych globałów
>>> variables = {'a': 6, 'b': 7}
>>> eval('a * b', globals=variables)
42
Dodatkowo, kod nie może przypadkowo odwoływać się do nazw zdefiniowanych na zewnątrz:
>>> eval('variables')
{'a': 6, 'b': 7}
>>> eval('variables', globals=variables)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'variables' is not defined
Użycie defaultdict
pozwala na przykład ustawić niezdefiniowane zmienne na zero:
>>> from collections import defaultdict
>>> variables = defaultdict(int, {'a': 42})
>>> eval('a * c', globals=variables) # note that 'c' is not explicitly defined
0
Ocena ciągu zawierającego literał w języku Python za pomocą ast.literal_eval
Jeśli masz ciąg znaków, który zawiera literały w języku Python, takie jak ciągi, ast.literal_eval
itp., Możesz użyć ast.literal_eval
do oceny jego wartości zamiast wartości eval
. Ma to dodatkową funkcję pozwalającą tylko na określoną składnię.
>>> import ast
>>> code = """(1, 2, {'foo': 'bar'})"""
>>> object = ast.literal_eval(code)
>>> object
(1, 2, {'foo': 'bar'})
>>> type(object)
<class 'tuple'>
Nie jest to jednak bezpieczne przy wykonywaniu kodu dostarczonego przez niezaufanego użytkownika, a zawieszenie interpretera przy starannie spreparowanym wejściu jest trywialne
>>> import ast
>>> ast.literal_eval('()' * 1000000)
[5] 21358 segmentation fault (core dumped) python3
Tutaj dane wejściowe to ciąg ()
powtórzony milion razy, co powoduje awarię parsera CPython. Programiści CPython nie uważają błędów w parserze za kwestie bezpieczeństwa.
Wykonywanie kodu dostarczonego przez niezaufanego użytkownika za pomocą exec, eval lub ast.literal_eval
Nie jest możliwe użycie eval
lub exec
do bezpiecznego wykonania kodu od niezaufanego użytkownika. Nawet ast.literal_eval
jest podatny na awarie w parserze. Czasami można zabezpieczyć się przed złośliwym wykonaniem kodu, ale nie wyklucza to możliwości bezwarunkowych awarii w parserze lub tokenizerze.
Aby ocenić kod przez niezaufanego użytkownika, musisz zwrócić się do jakiegoś modułu innej firmy lub napisać własny parser i własną maszynę wirtualną w Pythonie.