Saya telah melihat evaluasi dinamis kode Python, dan menemukan eval()
dan compile()
fungsi, dan exec
pernyataan itu.
Dapatkah seseorang tolong jelaskan perbedaan antara eval
dan exec
, dan bagaimana berbagai mode compile()
cocok?
Pada dasarnya, eval
digunakan untuk eval uate satu yang dihasilkan secara dinamis ekspresi Python, dan exec
digunakan untuk exec ute yang dihasilkan secara dinamis kode Python hanya untuk efek samping.
eval
dan exec
memiliki dua perbedaan ini:
eval
hanya menerima satu ekspresi , exec
dapat mengambil blok kode yang memiliki pernyataan Python: loop try: except:
,, class
dan fungsi / metode def
initions dan sebagainya.
Ekspresi dalam Python adalah apa pun yang Anda dapat miliki sebagai nilai dalam penugasan variabel:
a_variable = (anything you can put within these parentheses is an expression)
eval
mengembalikan nilai dari ekspresi yang diberikan, sedangkan exec
mengabaikan nilai kembali dari kodenya, dan selalu mengembalikan None
(dalam Python 2 itu adalah pernyataan dan tidak dapat digunakan sebagai ekspresi, jadi itu benar-benar tidak mengembalikan apa pun).
Dalam versi 1.0 - 2.7, exec
adalah pernyataan, karena CPython diperlukan untuk menghasilkan berbagai jenis objek kode untuk fungsi yang digunakan exec
untuk efek sampingnya di dalam fungsi.
Dalam Python 3, exec
adalah fungsi; penggunaannya tidak berpengaruh pada bytecode yang dikompilasi dari fungsi di mana ia digunakan.
Jadi pada dasarnya:
>>> 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
The compile
dalam 'exec'
modus mengkompilasi sejumlah pernyataan menjadi bytecode yang secara implisit selalu kembali None
, sedangkan di 'eval'
modus itu menyusun satu ekspresi menjadi bytecode yang kembali nilai ekspresi itu.
>>> 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
Dalam 'eval'
mode (dan dengan eval
fungsi jika sebuah string dilewatkan), compile
menimbulkan pengecualian jika kode sumber berisi pernyataan atau hal lain di luar satu ekspresi:
>>> 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
Sebenarnya pernyataan "eval hanya menerima satu ekspresi" hanya berlaku ketika sebuah string (yang berisi kode sumber Python ) diteruskan ke eval
. Kemudian secara internal dikompilasi ke bytecode menggunakan compile(source, '<string>', 'eval')
Di sinilah perbedaannya berasal.
Jika code
objek (yang berisi bytecode Python ) diteruskan ke exec
atau eval
, mereka berperilaku identik , kecuali untuk fakta yang exec
mengabaikan nilai kembali, masih None
selalu kembali . Jadi mungkin digunakan eval
untuk mengeksekusi sesuatu yang memiliki pernyataan, jika Anda hanya compile
mengubahnya menjadi bytecode sebelum alih-alih mengirimkannya sebagai string:
>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>
berfungsi tanpa masalah, meskipun kode yang dikompilasi berisi pernyataan. Itu masih kembali None
, karena itu adalah nilai kembali dari objek kode yang dikembalikan compile
.
Dalam 'eval'
mode (dan dengan eval
fungsi jika sebuah string dilewatkan), compile
menimbulkan pengecualian jika kode sumber berisi pernyataan atau hal lain di luar satu ekspresi:
>>> 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
dan eval
The exec
function (yang pernyataan di Python 2 ) digunakan untuk mengeksekusi pernyataan atau program yang dibuat secara dinamis:
>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
The eval
fungsi melakukan hal yang sama untuk ekspresi tunggal , dan mengembalikan nilai dari ekspresi:
>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
exec
dan eval
keduanya menerima program / ekspresi untuk dijalankan baik sebagai str
, unicode
atau bytes
objek yang mengandung kode sumber, atau sebagai code
objek yang berisi bytecode Python.
Jika a str
/ unicode
/ bytes
berisi kode sumber diteruskan ke exec
, itu berperilaku setara dengan:
exec(compile(source, '<string>', 'exec'))
dan eval
juga berperilaku setara dengan:
eval(compile(source, '<string>', 'eval'))
Karena semua ekspresi dapat digunakan sebagai pernyataan dalam Python (ini disebut Expr
node dalam tata bahasa abstrak Python ; kebalikannya tidak benar), Anda selalu dapat menggunakan exec
jika Anda tidak memerlukan nilai kembali. Artinya, Anda bisa menggunakan salah satu eval('my_func(42)')
atau exec('my_func(42)')
, perbedaannya adalah yang eval
mengembalikan nilai yang dikembalikan oleh my_func
, dan exec
membuangnya:
>>> 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
>>>
Dari 2, hanya exec
menerima kode sumber yang berisi pernyataan, seperti def
, for
, while
, import
, atau class
, pernyataan penugasan (alias a = 42
), atau seluruh program:
>>> 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
Keduanya exec
dan eval
menerima 2 argumen posisi tambahan - globals
dan locals
- yang merupakan lingkup variabel global dan lokal yang dilihat kode. Ini default ke globals()
dan locals()
dalam lingkup yang disebut exec
atau eval
, tetapi kamus apa pun dapat digunakan untuk globals
dan apa saja mapping
untuk locals
(termasuk dict
tentu saja). Ini dapat digunakan tidak hanya untuk membatasi / memodifikasi variabel yang dilihat kode, tetapi sering juga digunakan untuk menangkap variabel yang dibuat oleh exec
kode yang digunakan:
>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}
(Jika Anda menampilkan nilai keseluruhan g
, itu akan jauh lebih lama, karena exec
dan eval
menambahkan modul built-in __builtins__
ke global secara otomatis jika hilang).
Dalam Python 2, sintaks resmi untuk exec
pernyataan tersebut sebenarnya exec code in globals, locals
, seperti pada
>>> exec 'global a; a, b = 123, 42' in g, l
Namun sintaks alternatif exec(code, globals, locals)
selalu diterima juga (lihat di bawah).
compile
The compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
built-in dapat digunakan untuk mempercepat pemanggilan ulang kode yang sama dengan exec
atau eval
dengan menyusun sumber menjadi code
objek terlebih dahulu. The mode
parameter kontrol jenis kode fragmen compile
fungsi menerima dan jenis bytecode menghasilkan. Pilihannya adalah 'eval'
, 'exec'
dan 'single'
:
'eval'
mode mengharapkan satu ekspresi, dan akan menghasilkan bytecode bahwa ketika dijalankan akan mengembalikan nilai ekspresi itu :
>>> 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'
menerima segala jenis konstruksi python dari ekspresi tunggal ke seluruh modul kode, dan menjalankannya seolah-olah itu adalah pernyataan tingkat atas modul. Objek kode kembali 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'
adalah bentuk terbatas 'exec'
yang menerima kode sumber yang berisi pernyataan tunggal (atau beberapa pernyataan dipisahkan oleh ;
) jika pernyataan terakhir adalah pernyataan ekspresi, kode bytec yang dihasilkan juga mencetak repr
nilai dari ekspresi itu ke output standar (!) .
Sebuah if
- elif
- else
rantai, loop dengan else
, dan try
dengan yang except
, else
dan finally
blok dianggap sebagai pernyataan tunggal.
Sumber fragmen yang berisi 2 pernyataan tingkat atas adalah kesalahan untuk 'single'
, kecuali dalam Python 2 ada bug yang kadang-kadang memungkinkan beberapa pernyataan tingkat atas dalam kode; hanya yang pertama dikompilasi; sisanya diabaikan:
Dalam Python 2.7.8:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
>>> a
5
Dan dengan 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
Ini sangat berguna untuk membuat cangkang Python interaktif. Namun, nilai ekspresi tidak dikembalikan , bahkan jika Anda eval
menghasilkan kode.
Jadi perbedaan terbesar exec
dan eval
sebenarnya berasal dari compile
fungsi dan modenya.
Selain mengkompilasi kode sumber ke bytecode, compile
mendukung kompilasi pohon sintaksis abstrak (parse pohon kode Python) menjadi code
objek; dan kode sumber menjadi pohon sintaksis abstrak ( ast.parse
ditulis dengan Python dan panggilan biasa compile(source, filename, mode, PyCF_ONLY_AST)
); ini digunakan misalnya untuk memodifikasi kode sumber dengan cepat, dan juga untuk pembuatan kode dinamis, karena seringkali lebih mudah untuk menangani kode sebagai pohon node daripada baris teks dalam kasus kompleks.
Meskipun eval
hanya memungkinkan Anda untuk mengevaluasi string yang berisi ekspresi tunggal, Anda dapat eval
seluruh pernyataan, atau bahkan seluruh modul yang telah compile
d menjadi bytecode; yaitu, dengan Python 2, print
adalah pernyataan, dan tidak dapat eval
dipimpin langsung:
>>> 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
dengan 'exec'
modus menjadi code
objek dan Anda dapat eval
itu ; yang eval
fungsi akan kembali 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
Jika seseorang melihat ke dalam eval
dan exec
kode sumber di CPython 3, ini sangat jelas; mereka berdua memanggil PyEval_EvalCode
dengan argumen yang sama, satu-satunya perbedaan adalah yang exec
secara eksplisit kembaliNone
.
exec
antara Python 2 dan Python 3Salah satu perbedaan utama dalam Python 2 adalah itu exec
adalah pernyataan dan eval
merupakan fungsi bawaan (keduanya adalah fungsi bawaan pada Python 3). Ini adalah fakta yang diketahui bahwa sintaks resmi exec
dalam Python 2 adalah exec code [in globals[, locals]]
.
Tidak seperti mayoritas panduan porting Python 2-ke-3 tampaknya menyarankan , pernyataan dalam CPython 2 dapat juga digunakan dengan sintaks yang terlihat persis seperti pemanggilan fungsi di Python 3. Alasannya adalah bahwa Python 0.9.9 memiliki built-in dalam fungsi! Dan fungsi bawaan itu diganti dengan pernyataan di suatu tempat sebelum rilis Python 1.0 . exec
exec
exec(code, globals, locals)
exec
Karena diinginkan untuk tidak merusak kompatibilitas dengan Python 0.9.9, Guido van Rossum menambahkan retasan kompatibilitas pada tahun 1993 : jika code
itu adalah tuple dengan panjang 2 atau 3, dan globals
dan locals
tidak dimasukkan ke dalam exec
pernyataan sebaliknya, maka code
akan ditafsirkan seolah-olah elemen ke-2 dan ke-3 dari tuple adalah globals
dan locals
masing - masing. Retasan kompatibilitas tidak disebutkan bahkan dalam dokumentasi Python 1.4 (versi online paling awal yang tersedia) ; dan dengan demikian tidak diketahui banyak penulis panduan dan alat porting, sampai didokumentasikan lagi pada November 2012 :
Ekspresi pertama mungkin juga merupakan tuple dengan panjang 2 atau 3. Dalam hal ini, bagian opsional harus dihilangkan. Bentuknya
exec(expr, globals)
setara denganexec expr in globals
, sedangkan bentuknyaexec(expr, globals, locals)
setara denganexec expr in globals, locals
. Bentuk tupleexec
menyediakan kompatibilitas dengan Python 3, di manaexec
fungsi daripada pernyataan.
Ya, dalam CPython 2.7 bahwa itu dengan mudah disebut sebagai opsi kompatibilitas-maju (mengapa membingungkan orang-orang bahwa ada opsi kompatibilitas mundur sama sekali), padahal sebenarnya ada di sana untuk kompatibilitas mundur selama dua dekade .
Jadi sementara exec
adalah pernyataan dalam Python 1 dan Python 2, dan fungsi bawaan di Python 3 dan Python 0.9.9,
>>> exec("print(a)", globals(), {'a': 42})
42
telah memiliki perilaku yang sama dalam setiap versi Python yang dirilis secara luas; dan bekerja di Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) dan IronPython 2.6.1 juga (pujian kepada mereka mengikuti perilaku tidak berdokumen CPython erat).
Apa yang tidak dapat Anda lakukan di Pythons 1.0 - 2.7 dengan retas kompatibilitasnya, adalah menyimpan nilai pengembalian exec
ke dalam variabel:
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
(yang tidak akan berguna dalam Python 3, seperti exec
biasa mengembalikan None
), atau meneruskan referensi ke exec
:
>>> call_later(exec, 'print(42)', delay=1000)
File "<stdin>", line 1
call_later(exec, 'print(42)', delay=1000)
^
SyntaxError: invalid syntax
Mana pola yang mungkin digunakan seseorang, meskipun tidak mungkin;
Atau gunakan dalam pemahaman daftar:
>>> [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
yang merupakan penyalahgunaan pemahaman daftar (gunakan for
loop sebagai gantinya!).
[i for i in globals().values() if hasattr(i, '__call__')][0]
pernyataan atau ungkapan? Jika itu ekspresi, mengapa saya tidak bisa menggunakannya dengan@
sebagai dekorator?42
juga merupakan ekspresi, dan Anda tidak dapat menggunakannya dengan@
sebagai dekorator.decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE
; yaitu Anda tidak dapat menggunakan ekspresi sewenang-wenang sebagai dekorator, HANYA pengidentifikasi (mungkin bertitik), diikuti oleh argumen panggilan opsional.a = b = c
adalah pernyataan yang benar-benar valid, seperti halnya sisi kanannyab = c
- yang bukan merupakan ekspresi.exec
bukan ekspresi: pernyataan dalam Python 2.x, dan fungsi dalam Python 3.x. Ini mengkompilasi dan segera mengevaluasi pernyataan atau set pernyataan yang terkandung dalam string. Contoh:eval
adalah fungsi bawaan ( bukan pernyataan), yang mengevaluasi ekspresi dan mengembalikan nilai yang dihasilkan ekspresi. Contoh:compile
adalah versi tingkat lebih rendah dariexec
daneval
. Itu tidak menjalankan atau mengevaluasi pernyataan atau ekspresi Anda, tetapi mengembalikan objek kode yang dapat melakukannya. Mode adalah sebagai berikut:compile(string, '', 'eval')
mengembalikan objek kode yang akan dieksekusi telah Anda lakukaneval(string)
. Perhatikan bahwa Anda tidak dapat menggunakan pernyataan dalam mode ini; hanya satu (satu) ekspresi yang valid.compile(string, '', 'exec')
mengembalikan objek kode yang akan dieksekusi telah Anda lakukanexec(string)
. Anda dapat menggunakan sejumlah pernyataan di sini.compile(string, '', 'single')
sepertiexec
mode, tetapi akan mengabaikan segalanya kecuali untuk pernyataan pertama. Perhatikan bahwaif
/else
pernyataan dengan hasilnya dianggap sebagai pernyataan tunggal.sumber
exec()
sekarang sebenarnya adalah sebuah fungsi.exec
adalah pernyataan dalam versi yang Anda targetkan, menipu untuk memasukkan parens tersebut, dan jika Anda mencoba menggunakanin globals, locals
, juga buggy.exec
mendukung tanda kurung dan fungsi seperti doa dalam Python 2 .x = (y)
, itu mungkin benar. Pernyataan-berubah-fungsi lainnya adalahprint
; bandingkan hasilprint(1, 2, 3)
dalam python 2 dan 3.exec adalah untuk pernyataan dan tidak mengembalikan apa pun. eval adalah untuk ekspresi dan mengembalikan nilai ekspresi.
ekspresi berarti "sesuatu" sedangkan pernyataan berarti "melakukan sesuatu".
sumber