Szukałem na dynamiczną ocenę kodu Pythona i natknąć się eval()
i compile()
funkcji, a exec
stwierdzenie.
Czy ktoś mógłby wyjaśnić różnicę między eval
a exec
i jak różne tryby compile()
wpasować?
Zasadniczo eval
służy do eval uate pojedynczy dynamicznie generowane wyrażenie Python, i exec
służy do exec ute dynamicznie generowanego kodu Pythona tylko jego skutki uboczne.
eval
i exec
mają te dwie różnice:
eval
akceptuje tylko jeden wyraz , exec
mogą wziąć blok kodu, który ma oświadczenia Pythonie pętli try: except:
, class
a funkcja / metoda def
initions 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 gdy exec
ignoruje wartość zwracaną z kodu i zawsze zwraca None
(w Pythonie 2 jest to instrukcja i nie może być używana jako wyrażenie, więc tak naprawdę nic nie zwraca).
W wersjach 1.0 - 2.7 exec
była instrukcją, ponieważ CPython musiał stworzyć inny rodzaj obiektu kodu dla funkcji, które wykorzystywały exec
efekty uboczne wewnątrz funkcji.
W Pythonie 3 exec
jest funkcją; jego użycie nie ma wpływu na skompilowany kod bajtowy funkcji, w której jest używany.
Zatem 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
compile
W 'exec'
trybie kompiluje dowolną liczbę sprawozdań do kodu bajtowego, która w domyśle zawsze zwraca None
, natomiast w 'eval'
trybie on kompiluje jeden wyraz do kodu bajtowego, który powraca 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 'eval'
trybie (a więc z eval
funkcją, jeśli przekazywany jest ciąg znaków), compile
podnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza jednym 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 jedno wyrażenie” ma zastosowanie tylko wtedy, gdy przekazany jest ciąg znaków (zawierający kod źródłowy Pythona ) eval
. Następnie jest wewnętrznie kompilowany do kodu bajtowego za pomocą compile(source, '<string>', 'eval')
To właśnie stąd naprawdę pochodzi różnica.
Jeśli code
obiekt (który zawiera kod bajtowy Pythona ) jest przekazywany do exec
lub eval
, zachowują się identycznie , z wyjątkiem faktu, że exec
ignoruje zwracaną wartość, wciąż zwraca wartość None
. Możliwe jest więc użycie eval
czegoś, co zawiera instrukcje, jeśli po prostu compile
d wprowadzisz go do kodu bajtowego przed przekazaniem go jako łańcucha:
>>> 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 z obiektu kodu zwróconego compile
.
W 'eval'
trybie (a więc z eval
funkcją, jeśli przekazywany jest ciąg znaków), compile
podnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza jednym 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
exec
i eval
exec
Funkcja (co było oświadczenie w Pythonie 2 ) służy do wykonywania dynamicznie utworzony oświadczenia lub programu:
>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
eval
Funkcja robi to samo dla pojedynczego wyrazu , i zwraca wartość wyrażenia:
>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
exec
i eval
oba akceptują uruchamianie programu / wyrażenia jako str
, unicode
lub bytes
obiektu zawierającego kod źródłowy, lub jako code
obiekt zawierający kod bajtowy Pythona.
Jeśli przekazano kod źródłowy zawierający str
/ unicode
/ , zachowuje się on tak samo, jak:bytes
exec
exec(compile(source, '<string>', 'exec'))
i eval
podobnie zachowuje się jak:
eval(compile(source, '<string>', 'eval'))
Ponieważ wszystkie wyrażenia mogą być używane w Pythonie jako instrukcje (są one nazywane Expr
węzłami w gramatyce abstrakcyjnej Pythona ; odwrotność nie jest prawdą), zawsze możesz ich użyć, exec
jeśli nie potrzebujesz wartości zwracanej. Innymi słowy, możesz użyć albo, eval('my_func(42)')
albo z exec('my_func(42)')
tą różnicą, że eval
zwraca wartość zwróconą przez my_func
i 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 kodu źródłowego, który zawiera instrukcje, jak def
, for
, while
, import
, lub class
, oświadczenie przypisanie (aka a = 42
) lub całe programy:
>>> 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
Zarówno exec
i eval
przyjąć 2 dodatkowe argumenty pozycyjne - globals
i locals
- które są zmienne globalne i lokalne celownicze, że kod widzi. Domyślnie są w zakresie globals()
i locals()
w zakresie, który wywołał exec
lub eval
, ale można użyć dowolnego słownika globals
i mapping
dla dowolnego locals
(w tym dict
oczywiście). Można ich używać nie tylko do ograniczania / modyfikowania zmiennych, które widzi kod, ale często są również używane do przechwytywania zmiennych exec
tworzonych przez kod Uted:
>>> 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łości g
, byłaby ona znacznie dłuższa, ponieważ exec
i automatycznie eval
dodajesz wbudowany moduł __builtins__
globali, jeśli go brakuje).
W Pythonie 2 oficjalna składnia exec
instrukcji jest tak exec code in globals, locals
jak w
>>> exec 'global a; a, b = 123, 42' in g, l
Jednak alternatywna składnia exec(code, globals, locals)
również zawsze była akceptowana (patrz poniżej).
compile
compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
Wbudowanej może być stosowany w celu przyspieszenia powtarzających wywołania tego samego kodu z exec
lub eval
kompilując źródła do code
obiektu z wyprzedzeniem. Ten mode
parametr kontroluje rodzaj fragmentu kodu compile
akceptowanego przez funkcję oraz rodzaj tworzonego kodu bajtowego. Do wyboru są 'eval'
, 'exec'
i 'single'
:
'eval'
Tryb oczekuje pojedynczego wyrażenia i wygeneruje kod bajtowy, który po uruchomieniu zwróci wartość tego wyrażenia :
>>> 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 python od pojedynczych wyrażeń do całych modułów kodu i wykonuje je tak, jakby były modułowymi instrukcjami najwyższego poziomu. Obiekt kodu zwraca None
:
>>> 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 pojedynczą instrukcję (lub wiele instrukcji oddzielonych ;
), jeśli ostatnia instrukcja jest instrukcją wyrażenia, wynikowy kod bajtowy wypisujerepr
również wartość tego wyrażenia na standardowe wyjście (!) .
if
- elif
- else
łańcuch, pętla z else
oraz try
z jego except
, else
a finally
bloki są uważane za pojedynczy rachunek.
Fragment źródłowy zawierający 2 instrukcje najwyższego poziomu jest błędem 'single'
, z wyjątkiem tego , że w Pythonie 2 występuje błąd, który czasami dopuszcza wiele instrukcji najwyższego poziomu w kodzie; tylko pierwsza jest kompilowana; reszta jest ignorowana:
W Python 2.7.8:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
>>> a
5
Oraz w Python 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 nie jest zwracana , nawet jeśli eval
wynikowy kod.
Zatem największe rozróżnienie exec
i eval
faktycznie pochodzi od compile
funkcji i jej trybów.
Oprócz kompilowania kodu źródłowego do kodu bajtowego, compile
obsługuje kompilowanie abstrakcyjnych drzew składni (parsowanie drzew kodu Python) na code
obiekty; i kod źródłowy w abstrakcyjne drzewa składniowe ( ast.parse
jest napisany w Pythonie i po prostu 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ługiwać kod jako drzewo węzłów zamiast wierszy tekstu w skomplikowanych przypadkach.
Chociaż eval
pozwala tylko na ocenę ciągu zawierającego pojedyncze wyrażenie, możesz eval
całą instrukcję, a nawet cały moduł, który został compile
przekształcony w kod bajtowy; to znaczy, że w Pythonie 2 print
jest instrukcją i nie można jej eval
prowadzić bezpośrednio:
>>> 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
z 'exec'
trybem na code
obiekt i możesz to eval
zrobić ; eval
funkcja 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 patrzy się eval
i exec
kodu źródłowego w CPython 3, jest to bardzo widoczne; oba wywołują PyEval_EvalCode
z tymi samymi argumentami, jedyną różnicą jest to, że exec
jawnie zwracaNone
.
exec
między Python 2 i Python 3Jedną z głównych różnic w Pythonie 2 jest to, że exec
jest instrukcją i eval
jest funkcją wbudowaną (obie są funkcjami wbudowanymi w Pythonie 3). Powszechnie wiadomo, że oficjalna składnia exec
w języku Python 2 to exec code [in globals[, locals]]
.
W przeciwieństwie do większości przewodników portowania 2 do 3 w Pythonie wydaje się , że instrukcja w CPython 2 może być również używana ze składnią, która wygląda dokładnie tak , jak wywołanie funkcji w Pythonie 3. Powodem jest to, że Python 0.9.9 miał wbudowane w działaniu! I ta wbudowana funkcja została zastąpiona instrukcją gdzieś przed wydaniem Python 1.0 . exec
exec
exec(code, globals, locals)
exec
Ponieważ pożądane było, aby nie zerwać wstecznej zgodności z Pythonem 0.9.9, Guido van Rossum dodał hack kompatybilności w 1993 roku : jeśli byłby code
krotkę o długości 2 lub 3 globals
i locals
nie zostałby przekazany do exec
instrukcji, code
interpretacja byłaby interpretowana tak jakby drugim i trzecim elementem krotki były odpowiednio globals
i locals
. Hack kompatybilności nie został wspomniany nawet w dokumentacji Python 1.4 (najwcześniejsza dostępna wersja online) ; dlatego wielu pisarzy przewodników i narzędzi do przenoszenia nie było znane, dopóki nie zostało to ponownie udokumentowane w listopadzie 2012 r . :
Pierwszym wyrażeniem może być krotka o długości 2 lub 3. W takim przypadku części opcjonalne należy pominąć. Formularz
exec(expr, globals)
jest równoważnyexec expr in globals
, podczas gdy formularzexec(expr, globals, locals)
jest równoważnyexec expr in globals, locals
. Krotkowa formaexec
zapewnia zgodność z Pythonem 3, gdzieexec
jest funkcją, a nie instrukcją.
Tak, w CPython 2.7, który jest łatwo określany jako opcja kompatybilności wstecznej (po co mylić ludzi z tym, że w ogóle istnieje opcja kompatybilności wstecznej), skoro tak naprawdę istniała tam od dwóch dekad .
Zatem chociaż exec
istnieje instrukcja w Python 1 i Python 2, a wbudowana funkcja w Python 3 i Python 0.9.9,
>>> exec("print(a)", globals(), {'a': 42})
42
zachowywał się identycznie w każdej możliwej powszechnie wydanej wersji Pythona; i działa również w Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) i IronPython 2.6.1 (pochwały dla nich po nieudokumentowanym zachowaniu CPython).
To, czego nie można zrobić w Pythons 1.0 - 2.7 z jego hackem zgodności, to przechowywanie zwracanej wartości exec
w 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 nie byłoby przydatne w Pythonie 3, jak exec
zawsze zwraca None
), lub przekazanie odwołania do exec
:
>>> call_later(exec, 'print(42)', delay=1000)
File "<stdin>", line 1
call_later(exec, 'print(42)', delay=1000)
^
SyntaxError: invalid syntax
Który wzór mógł być rzeczywiście użyty, choć mało prawdopodobny;
Lub użyj go do zrozumienia listy:
>>> [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
co jest nadużyciem rozumienia listy ( for
zamiast tego użyj pętli!).
[i for i in globals().values() if hasattr(i, '__call__')][0]
oświadczenie lub wyrażenie? Jeśli to było wyrażenie, dlaczego nie mogę używać go@
jako dekoratora?42
jest również wyrażeniem i nie można go używać@
jako dekoratora.decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE
; tzn. nie można używać dowolnych wyrażeń jako dekoratorów, TYLKO (być może kropkowanego) identyfikatora, a następnie opcjonalnych argumentów wywołania.a = b = c
jest to całkowicie poprawne stwierdzenie, podobnie jak jego prawa stronab = c
- co nie jest wyrażeniem.exec
nie jest wyrażeniem: instrukcja w Pythonie 2.xi funkcja w Pythonie 3.x. Kompiluje i natychmiast ocenia instrukcję lub zestaw instrukcji zawartych w ciągu. Przykład:eval
jest funkcją wbudowaną ( nie jest instrukcją), która ocenia wyrażenie i zwraca wartość wytwarzaną przez to wyrażenie. Przykład:compile
jest niższą wersjąexec
ieval
. Nie wykonuje ani nie ocenia twoich instrukcji ani wyrażeń, 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żesz używać instrukcji w tym trybie; poprawne jest tylko (pojedyncze) wyrażenie.compile(string, '', 'exec')
zwraca obiekt kodu, który zostałby wykonany, gdybyś to zrobiłexec(string)
. Możesz użyć dowolnej liczby instrukcji tutaj.compile(string, '', 'single')
działa jakexec
tryb, ale zignoruje wszystko oprócz pierwszej instrukcji. Zauważ, że instrukcjaif
/else
z jej wynikami jest uważana za pojedynczą instrukcję.źródło
exec()
jest teraz funkcją.exec
jest stwierdzeniem w wersji, na którą celujesz, zwodnicze jest włączenie tych parenów, a jeśli spróbujesz użyćin globals, locals
, również błędny.exec
obsługuje nawiasy i działa jak wywołanie w Pythonie 2 .x = (y)
, to może być prawda. Inną funkcją przekształconą w instrukcję jestprint
; porównaj wynikprint(1, 2, 3)
wexec jest dla instrukcji i niczego nie zwraca. eval służy do wyrażenia i zwraca wartość wyrażenia.
wyrażenie oznacza „coś”, a wyrażenie oznacza „zrób coś”.
źródło