Jaka jest różnica między eval, exec i kompilacji?
Przyglądałem się dynamicznej ocenie kodu Pythona i natknąłem się na funkcje eval()
i compile()
oraz instrukcję exec
.
Czy ktoś może wyjaśnić różnicę między eval
i exec
i jak różne tryby compile()
pasują do siebie?
3 answers
Krótka odpowiedź, czyli TL;DR
Zasadniczo, eval
jest używany do evaluate pojedynczego dynamicznie generowanego wyrażenia Pythona, oraz exec
jest używany do execute dynamicznie generowany kod Pythona tylko dla jego efektów ubocznych.
eval
i exec
mają te dwie różnice:
-
eval
akceptuje tylko pojedyncze wyrażenie,exec
może przyjmować blok kodu zawierający polecenia Pythona: loops,try: except:
,class
i inicjały function / methoddef
i tak dalej.Wyrażenie w Pythonie jest tym, co możesz mieć jako wartość w przypisaniu zmiennej:
a_variable = (anything you can put within these parentheses is an expression)
eval
Zwraca wartość podanego wyrażenia, podczas gdyexec
ignoruje zwracaną wartość ze swojego kodu i zawsze zwracaNone
(w Pythonie 2 jest to polecenie i nie może być użyte jako wyrażenie, więc tak naprawdę niczego nie zwraca).
W wersji 1.0 - 2.7, exec
było stwierdzeniem, ponieważ CPython musiał stworzyć inny rodzaj obiektu kodu dla funkcji, które używały exec
do jego efektów ubocznych wewnątrz funkcji.
W Pythonie 3, {[25] } jest funkcją; jej użycie nie ma wpływu na skompilowany kod bajtowy funkcji, w której jest używana.
W zasadzie:
>>> a = 5
>>> eval('37 + a') # it is an expression
42
>>> exec('37 + a') # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47') # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47') # you cannot evaluate a statement
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
a = 47
^
SyntaxError: invalid syntax
Tryb compile
w trybie 'exec'
kompiluje dowolną liczbę poleceń do kodu bajtowego, który domyślnie zawsze zwraca None
, podczas gdy w 'eval'
tryb kompiluje pojedynczewyrażenie w Bajt kodzie, który zwraca wartość tego wyrażenia.
>>> eval(compile('42', '<string>', 'exec')) # code returns None
>>> eval(compile('42', '<string>', 'eval')) # code returns 42
42
>>> exec(compile('42', '<string>', 'eval')) # code returns 42,
>>> # but ignored by exec
W trybie 'eval'
(a więc z funkcją eval
Jeśli przekazywany jest łańcuch znaków), compile
podnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza pojedynczym wyrażeniem:
>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
W rzeczywistości Instrukcja "eval akceptuje tylko pojedyncze wyrażenie" stosuje się tylko wtedy, gdy łańcuch (który zawiera Python kod źródłowy ) jest przekazywany do eval
. Następnie jest wewnętrznie kompilowany do kodu bajtowego za pomocą compile(source, '<string>', 'eval')
to stąd naprawdę bierze się różnica.
Jeśli obiekt code
(który zawiera Python bajtowy kod ) zostanie przekazany do exec
lub eval
, zachowują się identycznie , z wyjątkiem faktu, że exec
ignoruje zwracaną wartość, wciąż zwracając None
zawsze. Więc możliwe jest użycie eval
do wykonania czegoś, co ma polecenia, jeśli tylko compile
d do bajtowego kodu przed zamiast przekazywania go jako ciąg znaków:
>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>
Działa bez problemów, mimo że skompilowany kod zawiera instrukcje. Nadal zwraca None
, ponieważ jest to wartość zwracana obiektu code zwróconego z compile
.
W trybie 'eval'
(a więc z funkcją eval
Jeśli przekazywany jest łańcuch znaków), compile
podnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza pojedynczym wyrażenie:
>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
Dłuższa odpowiedź, a.k. a krwawe szczegóły
exec
i eval
The exec
funkcja (która była Instrukcją w Pythonie 2 ) jest używana do wykonywania dynamicznie tworzonych instrukcji lub programu:
>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
The eval
funkcja robi to samo dla pojedynczego wyrażenia, i Zwraca wartość wyrażenia:
>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
exec
i eval
obie akceptują program/wyrażenie do uruchomienia jako str
, unicode
lub bytes
obiekt zawierający kod źródłowy, lub jako code
obiekt zawierający bajt kodu Pythona.
Jeśli str
/unicode
/bytes
zawiera kod źródłowy przekazany do exec
, zachowuje się równoważnie do:
exec(compile(source, '<string>', 'exec'))
I eval
podobnie zachowuje się odpowiednik:
eval(compile(source, '<string>', 'eval'))
Ponieważ wszystkie wyrażenia mogą być używane jako wyrażenia w Pythonie (nazywane są one węzłami Expr
w Pythonie streszczenie gramatyka; odwrotność nie jest prawdziwa), zawsze możesz użyć exec
, jeśli nie potrzebujesz zwracanej wartości. Oznacza to, że możesz użyć eval('my_func(42)')
lub exec('my_func(42)')
, z tą różnicą, że eval
Zwraca wartość zwróconą przez my_func
, a exec
odrzuca ją:
>>> def my_func(arg):
... print("Called with %d" % arg)
... return arg * 2
...
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>>
Z 2, tylko exec
akceptuje kod źródłowy, który zawiera instrukcje, takie jakdef
, for
, while
, import
, w 1998 roku, po raz pierwszy w historii, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce i za granicą.]}
>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
Oba exec
i eval
akceptują 2 dodatkowe argumenty pozycyjne - globals
i locals
- które są globalnymi i lokalnymi zakresami zmiennych, które widzi kod. Te domyślne to globals()
i locals()
w zakresie, który nazywa się exec
lub eval
, ale każdy słownik może być użyty dla globals
i dowolnego mapping
dla locals
(w tym oczywiście dict
). Mogą one być używane nie tylko do ograniczania / modyfikowania zmiennych, które widzi kod, ale często są również używane do przechwytywania zmiennych, które kod uted {25]} tworzy:
>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}
(jeśli wyświetlisz wartość całego g
, będzie to znacznie dłuższe, ponieważ exec
i eval
dodają wbudowany Moduł jako __builtins__
do globali automatycznie, jeśli go brakuje).
exec
jest w rzeczywistości exec code in globals, locals
, jak w
>>> exec 'global a; a, b = 123, 42' in g, l
Jednak składnia alternatywna exec(code, globals, locals)
również zawsze była akceptowana (patrz niżej).
compile
The compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
Wbudowany może być używany do przyspieszania powtarzających się wywołań tego samego kodu z exec
lub eval
przez kompilację źródła do obiektu code
. Parametr mode
kontroluje rodzaj fragmentu kodu akceptowanego przez funkcję compile
oraz rodzaj wytwarzanego przez nią bajtowego kodu. Wybory są 'eval'
, 'exec'
i 'single'
:
-
'eval'
mode oczekuje pojedynczego wyrażenia i wytworzy bajt, który po uruchomieniu zwróci wartość , która wyrażenie :>>> dis.dis(compile('a + b', '<string>', 'eval')) 1 0 LOAD_NAME 0 (a) 3 LOAD_NAME 1 (b) 6 BINARY_ADD 7 RETURN_VALUE
-
'exec'
akceptuje wszelkiego rodzaju konstrukcje Pythona, od pojedynczych wyrażeń po całe Moduły kodu, i wykonuje je tak, jakby były instrukcjami najwyższego poziomu modułu. Obiekt code zwracaNone
:>>> dis.dis(compile('a + b', '<string>', 'exec')) 1 0 LOAD_NAME 0 (a) 3 LOAD_NAME 1 (b) 6 BINARY_ADD 7 POP_TOP <- discard result 8 LOAD_CONST 0 (None) <- load None on stack 11 RETURN_VALUE <- return top of stack
-
'single'
jest ograniczoną formą'exec'
, która akceptuje kod źródłowy zawierający pojedyncze oświadczenie (lub wiele instrukcji oddzielonych;
) jeśli ostatnie oświadczenie jest wyrażeniem, również wypisujerepr
wartość tego wyrażenia na standardowe wyjście (!).An
if
-elif
-else
łańcuch, pętla zelse
i {[130] } z jegoexcept
,else
ifinally
bloki są uważane za pojedyncze stwierdzenie.Fragment źródłowy zawierający 2 wyrażenia najwyższego poziomu jest błędem
'single'
, z wyjątkiem Pythona 2 istnieje błąd , który czasami pozwala na wiele wyrażeń najwyższego poziomu w kodzie; tylko pierwszy jest skompilowany, reszta jest ignorowana:W Pythonie 2.7.8:
>>> exec(compile('a = 5\na = 6', '<string>', 'single')) >>> a 5
I w Pythonie 3.4.2:
>>> exec(compile('a = 5\na = 6', '<string>', 'single')) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1 a = 5 ^ SyntaxError: multiple statements found while compiling a single statement
Jest to bardzo przydatne do tworzenia interaktywnych powłok Pythona. Jednak wartość wyrażenia to not return , nawet jeśli
eval
kod wynikowy.
Tak więc największe rozróżnienie exec
i eval
pochodzi od compile
funkcji i jej trybów.
Oprócz kompilacji kodu źródłowego do bytecode, compile
obsługuje kompilowanie abstrakcyjne drzewa składniowe ([141] } jest napisany w Pythonie i wywołuje compile(source, filename, mode, PyCF_ONLY_AST)
); są one używane na przykład do modyfikowania kodu źródłowego w locie, a także do dynamicznego tworzenia kodu, ponieważ często łatwiej jest obsłużyć kod jako drzewo węzłów zamiast linii tekstu w złożonych przypadkach.
While eval
pozwala tylko na aby ocenić łańcuch zawierający pojedyncze wyrażenie, możesz eval
całą instrukcję lub nawet cały moduł, który został compile
d w bajtowym kodzie; to znaczy, w Pythonie 2, print
jest instrukcją i nie może być bezpośrednio wyprowadzony eval
:
>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print("Python is cool")
^
SyntaxError: invalid syntax
compile
go z trybem 'exec'
W obiekt code
i można eval
to ; funkcja eval
zwróci None
.
>>> code = compile('for i in range(3): print("Python is cool")',
'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is cool
Jeśli ktoś spojrzy na eval
oraz exec
kod źródłowy w CPython 3, to jest bardzo oczywiste; oba wywołują PyEval_EvalCode
z tymi samymi argumentami, jedyną różnicą jest to, że exec
jawnie zwraca None
.
Różnice w składni exec
pomiędzy Pythonem 2 a Pythonem 3
Jedna z głównych różnic w Pythonie 2 czy exec
jest instrukcją, a {[24] } jest wbudowaną funkcją (obie są wbudowanymi funkcjami w Pythonie 3).
Jest dobrze znany fakt, że oficjalna składnia exec
w Pythonie 2 to exec code [in globals[, locals]]
.
Większość Pythona 2-to-3 portowanie Przewodniki seem aby zasugerować , Instrukcja exec
W CPython 2 może być również używana ze składnią, która wygląda dokładnie jak wywołanie funkcji exec
w Pythonie 3. Powodem jest to, że Python 0.9.9 miał wbudowaną funkcję exec(code, globals, locals)
! I ta wbudowana funkcja została zastąpiona exec
instrukcją gdzieś przed wydaniem Pythona 1.0.
Ponieważ pożądane było nie łamać wstecznej kompatybilności z Pythonem 0.9.9, Guido van Rossum dodał hack zgodności w 1993: jeśli code
była krotką o długości 2 lub 3, a globals
i locals
nie zostały przekazane do exec
w przeciwnym razie, code
będzie interpretowane tak, jakby drugi i trzeci element krotki były odpowiednio globals
i {92]}. Hakowanie zgodności nie zostało wspomniane nawet w dokumentacji Pythona 1.4 (Najwcześniejsza dostępna wersja online) ; i tak było nie jest znany wielu pisarzom przewodników i narzędzi do przenoszenia, dopóki nie został udokumentowany ([385]} {191]} ponownie ([387]} w listopadzie 2012:
Pierwsze wyrażenie może być krotką o długości 2 lub 3. W takim przypadku należy pominąć części opcjonalne. Forma
exec(expr, globals)
jest równoważnaexec expr in globals
, natomiast formaexec(expr, globals, locals)
jest równoważnaexec expr in globals, locals
. Forma krotkiexec
zapewnia kompatybilność z Pythonem 3, gdzieexec
jest funkcją, a nie oświadczenie.
Tak, w CPython 2.7, że jest to handly określane jako opcja forward-compatibility (po co mylić ludzi o to, że istnieje opcja kompatybilności wstecznej w ogóle), kiedy to faktycznie było tam dla wstecznej kompatybilności przez dwie dekady .
W Pythonie 1 i Pythonie 2, a wbudowana funkcja w Pythonie 3 i Pythonie 0.9.9, {198]}>>> exec("print(a)", globals(), {'a': 42})
42
Miał identyczne zachowanie w możliwie każdym szeroko wydana wersja Pythona ever; działa również w Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) i IronPython 2.6.1 (chwała im za nieudokumentowane zachowanie CPython).
W Pythons 1.0 - 2.7 nie można zapisać wartości zwracanejexec
do zmiennej:
Python 2.7.11+ (default, Apr 17 2016, 14:00:29)
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
File "<stdin>", line 1
a = exec('print(42)')
^
SyntaxError: invalid syntax
(co również nie byłoby przydatne w Pythonie 3, ponieważ exec
zawsze zwraca None
)), lub przekazać odwołanie do exec
:
>>> call_later(exec, 'print(42)', delay=1000)
File "<stdin>", line 1
call_later(exec, 'print(42)', delay=1000)
^
SyntaxError: invalid syntax
Które a wzór, którego ktoś mógł użyć, choć mało prawdopodobny; {]}
Lub użyć go w spisie treści:
>>> [exec(i) for i in ['print(42)', 'print(foo)']
File "<stdin>", line 1
[exec(i) for i in ['print(42)', 'print(foo)']
^
SyntaxError: invalid syntax
Czyli nadużywanie składanych list (zamiast tego użyj pętli for
!).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-10 16:47:39
-
exec
is not an expression: a statement in Python 2.x oraz funkcję w Pythonie 3.x. kompiluje i natychmiast ocenia polecenie lub zestaw instrukcji zawartych w łańcuchu znaków. Przykład:exec('print(5)') # prints 5. # exec 'print 5' if you use Python 2.x, nor the exec neither the print is a function there exec('print(5)\nprint(6)') # prints 5{newline}6. exec('if True: print(6)') # prints 6. exec('5') # does nothing and returns nothing.
-
eval
jest wbudowaną funkcją (, a nie instrukcją), która oblicza wyrażenie i zwraca wartość, którą wytworzy wyrażenie. Przykład:x = eval('5') # x <- 5 x = eval('%d + 6' % x) # x <- 11 x = eval('abs(%d)' % -100) # x <- 100 x = eval('x = 5') # INVALID; assignment is not an expression. x = eval('if 1: x = 4') # INVALID; if is a statement, not an expression.
-
compile
jest wersją niższego poziomuexec
ieval
. Nie wykonuje ani oceniaj swoje wyrażenia lub wyrażenia, ale zwraca obiekt kodu, który może to zrobić. Tryby są następujące: -
compile(string, '', 'eval')
zwraca obiekt kodu, który zostałby wykonany, gdybyś to zrobiłeval(string)
. Zauważ, że nie można używać instrukcji w tym trybie; tylko (pojedyncze) wyrażenie jest ważne. -
compile(string, '', 'exec')
zwraca obiekt kodu, który zostałby wykonany, gdybyś to zrobiłexec(string)
. Możesz użyć dowolnej liczby wyrażeń tutaj. -
compile(string, '', 'single')
jest podobny do trybuexec
, ale oczekuje dokładnie jednego wyrażenia/instrukcji, npcompile('a=1 if 1 else 3', 'myf', mode='single')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-31 09:22:29
Exec jest dla instrukcji i nie zwraca niczego. eval służy do wyrażenia i zwraca wartość wyrażenia.
Wyrażenie oznacza "coś", podczas gdy wyrażenie oznacza "zrób coś".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-31 04:02:10