2.34. Kode dinamis¶
Tiga built-in mengambil string kode sumber Python dan menjalankannya: eval(), exec(), dan compile(). Bersama-sama, mereka memungkinkan kode untuk membangun dan mengeksekusi lebih banyak kode saat runtime -- yang terkadang memang merupakan alat yang tepat, tetapi jauh lebih sering menjadi sumber bug dan celah keamanan.
Peringatan
Fungsi-fungsi ini mengeksekusi Python sembarang. Meneruskan input pengguna ke eval atau exec -- string dari file yang dapat diedit pengguna, payload yang diterima melalui jaringan, nilai yang diketik pada prompt REPL -- memungkinkan input tersebut melakukan apapun yang dapat dilakukan skrip pemanggil, termasuk menghapus setiap file di perangkat. Gunakan secara sengaja, jangan pernah di dalam kode yang berjalan otomatis, dan jangan pernah pada data yang tidak Anda kendalikan.
2.34.1. eval¶
eval() menjalankan satu ekspresi dan mengembalikan nilainya:
>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'
Ekspresi melihat global dan lokal pemanggil secara default, itulah mengapa name dapat di-resolve dalam contoh kedua. Meneruskan kamus eksplisit memungkinkan Anda menyandbox evaluasi:
eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})
Meski tersandbox, eval tetap berbahaya. Ada teknik yang sudah dikenal untuk melarikan diri dari sandbox seperti itu; jangan mengandalkan trik __builtins__ kosong saja untuk input yang tidak dipercaya.
2.34.2. exec¶
exec() menjalankan blok kode bukan ekspresi tunggal -- pernyataan, definisi fungsi, loop -- dan mengembalikan None:
exec("for i in range(3): print(i)")
Output:
0
1
2
Blok dapat mendefinisikan nama yang tersedia setelahnya, dengan beberapa catatan tentang scope lokal vs global. exec di dalam fungsi jarang berperilaku seperti yang diharapkan penulisnya; jika Anda membutuhkannya, jalankan di tingkat modul.
2.34.3. compile¶
compile() mengubah string sumber menjadi objek kode yang dapat diteruskan ke eval() atau exec() nanti. Gunakan ketika sumber yang sama akan dijalankan berkali-kali -- penguraian terjadi sekali, eksekusi lebih cepat:
expr = compile("x * x", "<expr>", "eval")
for x in range(5):
print(eval(expr))
Output:
0
1
4
9
16
Argumen kedua adalah label yang muncul dalam traceback jika kode memunculkan pengecualian. Argumen ketiga adalah "eval" untuk ekspresi tunggal, "exec" untuk blok, atau "single" untuk pernyataan gaya interaktif yang mencetak hasilnya.
2.34.4. Kapan menggunakan fungsi-fungsi ini¶
Hampir tidak pernah. Sebagian besar kasus penggunaan yang dibayangkan pemula memiliki alternatif yang lebih aman:
Membaca file konfigurasi. Gunakan
json-- data terstruktur, tanpa eksekusi.Mengevaluasi nilai numerik yang diketik pengguna. Gunakan
int()/float()untuk mengurai, lalu aritmatika. Jika pengguna benar-benar perlu memasukkan formula, gunakan parser ekspresi kecil, bukaneval.
Jika Anda memang membutuhkan eval / exec / compile, isolasi titik pemanggilan, catat string yang akan dieksekusi, dan perlakukan sumber tersebut sebagai hal yang paling mencurigakan dalam kode Anda.