Apa yang dilakukan oleh eval () Python?

306

Dalam buku yang saya baca di Python, ia terus menggunakan kode eval(input('blah'))

Saya membaca dokumentasi, dan saya memahaminya, tetapi saya masih tidak melihat bagaimana itu mengubah input()fungsinya.

Apa fungsinya? Bisakah seseorang menjelaskan?

Billjk
sumber
4
Fungsi Eval mencoba mengeksekusi dan menafsirkan string (argumen) yang diteruskan kepadanya sebagai kode python. x = 1 cetak (eval ('x + 1')) Output dari kode di atas akan 2. Kelemahan dari pendekatan tersebut adalah, pengguna mendapatkan independensi penulisan kode yang dapat mengakibatkan kondisi kekacauan. Meskipun Anda dapat membatasi pengguna dari mengakses banyak variabel dan metode dengan mengirimkan parameter global dan lokal dalam fungsi eval.
ATIF IBAD KHAN

Jawaban:

276

Fungsi eval memungkinkan program Python menjalankan kode Python di dalam dirinya sendiri.

contoh eval (shell interaktif):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
BYS2
sumber
25
haha, itu adalah contoh sepele, tetapi Anda bisa membiarkan pengguna mengetik perintah sewenang-wenang dan meminta python menjalankannya. Jadi Anda bisa memiliki tipe pengguna dalam string perintah dan kemudian minta python menjalankannya sebagai kode. Jadi misalnya: eval ("__ import __ ('os'). Hapus ('file')").
BYS2
60
Tampaknya tidak ada gunanya sampai Anda menemukan kebutuhan untuk itu. Ini digunakan di situs-situs seperti codepad.org untuk memungkinkan Anda mengeksekusi skrip di lingkungan pengujian. eval()juga dapat digunakan untuk mengeksekusi kode yang sangat dinamis, tetapi Anda harus membuat diri Anda sepenuhnya sadar akan risiko keamanan dan kinerja sebelum menggunakannya.
George Cummins
6
@ GeorgeCummins, codepad.org tidak menggunakan eval, juga tidak bisa melakukan apa yang dilakukannya eval.
Mike Graham
16
@ GeorgeCummins: codepag.org menjalankan semuanya di kotak pasir: penjara chroot dengan pemeriksaan ptrace di mesin virtual untuk mencegah kode jahat melakukan hal buruk. Jauh lebih rumit daripada eval sederhana. Juga, eval adalah khusus-Python. codepad mendukung banyak bahasa.
FogleBird
4
@ GeorgeCummins, codepad menjalankan sistem yang sangat kompleks untuk menjalankan program sewenang-wenang dengan aman. eval, selain tidak aman, tidak dapat menjalankan seluruh program seperti yang dilakukan codepad karena hanya dapat mengevaluasi satu ekspresi.
Mike Graham
165

eval()menafsirkan string sebagai kode. Alasan mengapa begitu banyak orang telah memperingatkan Anda tentang penggunaan ini adalah karena pengguna dapat menggunakan ini sebagai opsi untuk menjalankan kode di komputer. Jika Anda telah eval(input())dan osmengimpor, seseorang dapat mengetikinput() os.system('rm -R *') mana akan menghapus semua file Anda di direktori home Anda. (Dengan asumsi Anda memiliki sistem unix). Menggunakan eval()adalah lubang keamanan. Jika Anda perlu mengonversi string ke format lain, coba gunakan hal-hal yang melakukannya, seperti int().

Kopi Kering
sumber
14
Maksudmu menggunakan evaldengan input()adalah lubang keamanan. Jangan memasukkan input()pernyataan eval dan Anda akan baik-baik saja.
Rohmer
19
@Rohmer, data yang tidak aman dapat berasal dari mana saja: permintaan web, formulir input bidang, membaca file, ... tidak hanya dari input konsol. Bahkan jika Anda menulis file sendiri, masih dapat berisi input yang awalnya berasal dari sumber yang tidak dipercaya. Begitu evaljuga masalah keamanan dalam banyak kasus.
sanderd17
3
karena inputbiasanya mengambil datanya dari konsol, pengguna dapat keluar dari program dan mengetikkan rm -R *...
cz
63

Banyak jawaban bagus di sini, tetapi tidak ada yang menjelaskan penggunaan eval()dalam konteksnya globalsdan localskwarg, yaitu eval(expression, globals=None, locals=None)(lihat dokumen untuk di eval sini ).

Ini dapat digunakan untuk membatasi fungsi yang tersedia melalui evalfungsi. Misalnya jika Anda memuat juru bahasa python baru locals()dan globals()akan sama dan terlihat seperti ini:

>>>globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
 '__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
 '__package__': None, '__name__': '__main__'}

Tentu saja ada fungsi-fungsi di dalam builtinsmodul yang dapat merusak sistem secara signifikan. Tetapi dimungkinkan untuk memblokir apa saja dan segala sesuatu yang tidak kita inginkan tersedia. Mari kita ambil contoh. Katakanlah kita ingin membuat daftar untuk merepresentasikan domain dari inti yang tersedia pada suatu sistem. Bagi saya, saya memiliki 8 core sehingga saya ingin daftar [1, 8].

>>>from os import cpu_count
>>>eval('[1, cpu_count()]')
[1, 8]

Demikian juga semua __builtins__tersedia.

>>>eval('abs(-1)')
1

Baik. Jadi di sana kita melihat satu fungsi yang kita inginkan terpapar dan contoh dari satu (dari banyak yang bisa menjadi jauh lebih kompleks) metode yang kita tidak ingin terpapar. Jadi mari kita blokir semuanya.

>>>eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable

Kami telah secara efektif memblokir semua __builtins__fungsi dan dengan demikian membawa tingkat perlindungan ke sistem kami. Pada titik ini kita dapat mulai menambahkan kembali fungsi yang ingin kita tampilkan.

>>>from os import cpu_count
>>>exposed_methods = {'cpu_count': cpu_count}
>>>eval('cpu_count()', {'__builtins__':None}, exposed_methods)
8
>>>eval('abs(cpu_count())', {'__builtins__':None}, exposed_methods)
TypeError: 'NoneType' object is not subscriptable

Sekarang kami memiliki cpu_countfungsi yang tersedia sementara masih memblokir semua yang tidak kami inginkan. Menurut pendapat saya, ini sangat kuat dan jelas dari ruang lingkup jawaban lain, bukan implementasi umum. Ada banyak kegunaan untuk sesuatu seperti ini dan selama itu ditangani dengan benar, saya pribadi merasaeval dapat digunakan dengan aman untuk nilai yang besar.

NB

Hal lain yang keren tentang ini kwargsadalah Anda dapat mulai menggunakan singkatan untuk kode Anda. Katakanlah Anda menggunakan eval sebagai bagian dari pipeline untuk mengeksekusi beberapa teks yang diimpor. Teks tidak perlu memiliki kode yang tepat, dapat mengikuti beberapa format file templat, dan masih menjalankan apa pun yang Anda inginkan. Sebagai contoh:

>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
Grr
sumber
29

Dalam Python 2.x input(...)setara dengan eval(raw_input(...)), dalam Python 3.x raw_inputdiganti namanya input, yang saya curiga menyebabkan kebingungan Anda (Anda mungkin melihat dokumentasi untuk inputPython 2.x). Selain itu, eval(input(...))akan berfungsi dengan baik di Python 3.x, tetapi akan menaikkan TypeErrordalam Python 2.

Dalam hal evalini digunakan untuk memaksa string yang dikembalikan dari inputmenjadi ekspresi dan ditafsirkan. Umumnya ini dianggap praktik buruk.

Zeekay
sumber
Atau itu adalah buku Python 3.x, di mana inputberarti apa yang raw_inputdilakukan dalam 2.x.
dan04
1
Ya itu terpikir oleh saya setelah saya menulis jawaban awal saya, dan itu jelas terjadi.
zeekay
6

Mungkin contoh yang menyesatkan dari membaca garis dan menafsirkannya.

Coba eval(input())dan ketik "1+1"- ini harus dicetak 2. Eval mengevaluasi ekspresi.

hburde
sumber
Mengapa saya harus mengetiknya di antara tanda kutip? Input mendapatkan string, dan meneruskannya ke eval, tidak mengeksekusi kode, jadi saya akan baik-baik saja jika saya hanya mengetik 1 + 1 ... ¿?
JC Rocamonde
Masalahnya adalah Anda mencampur P2.x dan 3.x. Dalam Python 2 kode Anda berfungsi, tetapi tidak masuk akal untuk mengevaluasi dua kali. Dalam python 3 tidak, dan mengembalikan sebuah string.
JC Rocamonde
6

eval()mengevaluasi string yang diteruskan sebagai ekspresi Python dan mengembalikan hasilnya. Misalnya, eval("1 + 1")menafsirkan dan mengeksekusi ekspresi"1 + 1" dan mengembalikan hasilnya (2).

Salah satu alasan Anda mungkin bingung adalah karena kode yang Anda kutip melibatkan tingkat tipuan. Panggilan fungsi bagian dalam (input) dijalankan terlebih dahulu sehingga pengguna melihat prompt "bla". Mari kita bayangkan mereka merespons dengan "1 + 1" (tanda kutip ditambahkan untuk kejelasan, jangan mengetiknya saat menjalankan program Anda), fungsi input mengembalikan string itu, yang kemudian diteruskan ke fungsi luar (eval) yang mengartikan string dan mengembalikan hasilnya (2).

Baca lebih lanjut tentang eval di sini .

Marc Cohen
sumber
6

eval(), seperti namanya, mengevaluasi argumen yang diteruskan.

raw_input()sekarang input()dalam versi python 3.x. Jadi contoh yang paling umum ditemukan untuk penggunaan eval()adalah penggunaannya untuk menyediakan fungsionalitas ituinput() disediakan dalam versi 2.x dari python. raw_input mengembalikan data yang dimasukkan pengguna sebagai string, sementara input mengevaluasi nilai data yang dimasukkan dan mengembalikannya.

eval(input("bla bla")) dengan demikian mereplikasi fungsi dari input() dalam 2.x, yaitu mengevaluasi data yang dimasukkan pengguna.

Singkatnya: eval()mengevaluasi argumen yang diterima dan karenanya eval('1 + 1')mengembalikan 2.

Rubal
sumber
6

Salah satu aplikasi yang berguna eval()adalah untuk mengevaluasi ekspresi python dari string. Misalnya memuat dari representasi string file kamus:

running_params = {"Greeting":"Hello "}
fout = open("params.dat",'w')
fout.write(repr(running_params))
fout.close()

Bacakan sebagai variabel dan edit:

fin = open("params.dat",'r')
diction=eval(fin.read())
diction["Greeting"]+="world"
fin.close()
print diction

Keluaran:

{'Greeting': 'Hello world'}
Nikolay Frick
sumber
7
Bagaimana ini menjawab pertanyaan yang menanyakan apa eval?
jkd
4

Saya terlambat menjawab pertanyaan ini, tetapi, sepertinya tidak ada yang memberikan jawaban yang jelas untuk pertanyaan itu.

Jika pengguna memasukkan nilai numerik, input()akan mengembalikan string.

>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'

Jadi, eval()akan mengevaluasi nilai yang dikembalikan (atau ekspresi) yang merupakan string dan return integer / float.

>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>> 
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14

Tentu saja ini adalah praktik yang buruk. int()atau float()harus digunakan sebagai ganti eval()dalam hal ini.

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Calvin Kim
sumber
3

Opsi lain jika Anda ingin membatasi string evaluasi menjadi literal sederhana adalah menggunakan ast.literal_eval(). Beberapa contoh:

import ast

# print(ast.literal_eval(''))          # SyntaxError: unexpected EOF while parsing
# print(ast.literal_eval('a'))         # ValueError: malformed node or string
# print(ast.literal_eval('import os')) # SyntaxError: invalid syntax
# print(ast.literal_eval('1+1'))       # 2: but only works due to a quirk in parser
# print(ast.literal_eval('1*1'))       # ValueError: malformed node or string
print(ast.literal_eval("{'a':1}"))     # {'a':1}

Dari dokumen :

Mengevaluasi simpul ekspresi atau string yang berisi tampilan Python literal atau wadah dengan aman. String atau simpul yang disediakan mungkin saja terdiri dari struktur literal Python berikut: string, byte, angka, tupel, daftar, dikte, set, boolean, dan Tidak ada.

Ini dapat digunakan untuk mengevaluasi string yang mengandung nilai-nilai Python dari sumber yang tidak dipercaya dengan aman tanpa harus menguraikan nilai-nilai itu sendiri. Itu tidak mampu mengevaluasi ekspresi kompleks sewenang-wenang, misalnya melibatkan operator atau pengindeksan.

Adapun alasannya sangat terbatas, dari milis :

Memungkinkan ekspresi operator dengan literal dimungkinkan, tetapi jauh lebih kompleks daripada implementasi saat ini. Implementasi sederhana tidak aman: Anda pada dasarnya dapat menginduksi penggunaan CPU dan memori yang tidak terikat tanpa usaha (coba "9 ** 9 ** 9" atau "[Tidak Ada] * 9 ** 9").

Sedangkan untuk kegunaannya, fungsi ini berguna untuk "membaca kembali" nilai-nilai dan wadah literal seperti yang dirubah oleh repr (). Misalnya ini dapat digunakan untuk serialisasi dalam format yang mirip tetapi lebih kuat dari JSON.

Brian Burns
sumber
1
ast.literal_evaltidak mendukung operator, bertentangan dengan '1+1'contoh Anda . Meskipun demikian itu memang mendukung daftar, angka, string dll, dan merupakan alternatif yang baik untuk evalkasus penggunaan umum .
benjimin
@benjimin oh Anda benar - itu hanya kekhasan bahwa ia menerima 1 +1! stackoverflow.com/questions/40584417/…
Brian Burns