Bagaimana cara menjalankan string yang berisi kode Python di Python?

357

Bagaimana cara menjalankan string yang berisi kode Python di Python?

hekevintran
sumber
5
Agar adil, tidak seperti pertanyaan lain, ini bukan duplikat. Dan yang lain telah memposting pertanyaan yang jauh lebih mendasar dari ini.
DNS
8
Jawaban yang benar, tentu saja, hampir selalu "tidak!".
bobince
23
@ S. Lott: Belum membaca FAQ baru-baru ini? "Juga baik-baik saja untuk bertanya dan menjawab pertanyaan pemrograman Anda sendiri, tetapi berpura-pura Anda berada di Jeopardy: frase itu dalam bentuk pertanyaan". Ini bukan pertanyaan yang buruk. +1
Devin Jeanpierre
49
@ S.Lott: Tidak. Anda tidak perlu melakukannya. Jika pertanyaannya belum ada di situs, maka itu adalah permainan yang adil (menurut FAQ, sebagaimana telah ditunjukkan). Jawab saja setiap pertanyaan seolah-olah OP membutuhkan bantuan. Mereka mungkin tidak, tetapi pria berikutnya yang membaca pertanyaan mereka mungkin akan melakukannya. Hanya 2 sen saya.
Bill the Lizard
1
Untuk semua yang Anda katakan tidak melakukannya: Saya punya kasus di mana saya benar-benar perlu. Ini melibatkan pemrosesan terdistribusi.
sudo

Jawaban:

331

Untuk pernyataan, gunakan exec(string)(Python 2/3) atau exec string(Python 2):

>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world

Saat Anda membutuhkan nilai ekspresi, gunakan eval(string):

>>> x = eval("2+2")
>>> x
4

Namun, langkah pertama adalah bertanya pada diri sendiri apakah Anda benar-benar perlu. Kode yang dieksekusi umumnya harus menjadi posisi terakhir: Ini lambat, jelek dan berbahaya jika dapat berisi kode yang dimasukkan pengguna. Anda harus selalu melihat alternatif terlebih dahulu, seperti fungsi tingkat tinggi, untuk melihat apakah ini dapat lebih memenuhi kebutuhan Anda.

Brian
sumber
1
tapi bagaimana dengan ruang lingkup kode yang dieksekusi oleh 'exec'? apakah itu bersarang?
jondinham
21
Kasus umum di mana seseorang ingin menggunakan 'exec' adalah sesuatu seperti if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42dll, yang kemudian mereka tulis sebagai exec ("x.%s = 42" % s). Untuk kasus umum ini (di mana Anda hanya perlu mengakses atribut objek yang disimpan dalam sebuah string), ada fungsi yang jauh lebih cepat, lebih bersih, dan lebih aman getattr: menulis saja getattr(x, s) = 42artinya hal yang sama.
ShreevatsaR
5
Bagaimana exec lebih lambat dari interpreter python?
Cris Stringfellow
6
@ ShreevatsaR bukan maksud Anda setattr(x, s, 42)? Saya mencoba getattr(x, 2) = 42dan gagal dengancan't assign to function call: <string>, line 1
Tanner Semerad
6
@ Tanner: Hmm. Ya memang setattr(x, s, 42)sintaks yang tepat. Terkejut butuh waktu lama untuk kesalahan itu ditangkap. Pokoknya, intinya adalah itu getattrdan setattrmerupakan alternatif execketika semua yang Anda inginkan adalah untuk mendapatkan anggota yang sewenang-wenang, dicari dengan tali.
ShreevatsaR
68

Dalam contoh string dieksekusi sebagai kode menggunakan fungsi exec.

import sys
import StringIO

# create file-like string to capture output
codeOut = StringIO.StringIO()
codeErr = StringIO.StringIO()

code = """
def f(x):
    x = x + 1
    return x

print 'This is my output.'
"""

# capture output and errors
sys.stdout = codeOut
sys.stderr = codeErr

exec code

# restore stdout and stderr
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

print f(4)

s = codeErr.getvalue()

print "error:\n%s\n" % s

s = codeOut.getvalue()

print "output:\n%s" % s

codeOut.close()
codeErr.close()
hekevintran
sumber
1
bertukar stdout dan stderr seperti itu membuat saya sangat gugup. ini sepertinya bisa menyebabkan masalah keamanan besar. apakah ada jalan lain?
Narcolapser
1
@Narcolapser, Anda harus menjadi mych lebih peduli dengan menggunakan execsama sekali (kecuali Anda tahu string kode berasal dari sumber tepercaya).
bruno desthuilliers
27

evaldan execmerupakan solusi yang tepat, dan mereka dapat digunakan dengan cara yang lebih aman .

Sebagaimana dibahas dalam manual referensi Python dan dijelaskan dengan jelas dalam tutorial ini , evaldan execfungsinya mengambil dua parameter tambahan yang memungkinkan pengguna untuk menentukan fungsi dan variabel global dan lokal apa yang tersedia.

Sebagai contoh:

public_variable = 10

private_variable = 2

def public_function():
    return "public information"

def private_function():
    return "super sensitive information"

# make a list of safe functions
safe_list = ['public_variable', 'public_function']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
# add any needed builtins back in
safe_dict['len'] = len

>>> eval("public_variable+2", {"__builtins__" : None }, safe_dict)
12

>>> eval("private_variable+2", {"__builtins__" : None }, safe_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'private_variable' is not defined

>>> exec("print \"'%s' has %i characters\" % (public_function(), len(public_function()))", {"__builtins__" : None}, safe_dict)
'public information' has 18 characters

>>> exec("print \"'%s' has %i characters\" % (private_function(), len(private_function()))", {"__builtins__" : None}, safe_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'private_function' is not defined

Intinya Anda mendefinisikan namespace di mana kode akan dieksekusi.

alan
sumber
9
Tidak mungkin membuat eval aman: Eval benar-benar berbahaya . Jika Anda mengambil kode dari saya dan mengevaluasi itu, saya dapat memisahkan program Python Anda. Permainan telah berakhir.
Ned Batchelder
3
@ v.oddou Saya merespons pernyataan Alan, "eval dan eksekutif .. dapat digunakan dengan cara yang aman." Ini salah. Jika seseorang berkata, "bash dapat digunakan dengan cara yang aman," itu juga akan salah. Bash berbahaya. Ini adalah bahaya yang perlu, tetapi tetap berbahaya. Mengklaim bahwa eval dapat dibuat aman sama sekali salah.
Ned Batchelder
1
@NedBatchelder ya memang. dan tautan yang Anda tuju adalah materi yang bagus. dengan kekuatan datang tanggung jawab, jadi intinya adalah mewaspadai potensi kekuatan eval. dan jika kita memutuskan kekuatan itu = bahaya.
v.oddou
3
@NedBatchelder Banyak potongan kode yang ditulis dengan Python bisa berbahaya juga, tetapi mengapa Anda menganggap itu evalatau execdimaksudkan untuk digunakan exec(input("Type what you want"))? Ada banyak kasus di mana suatu program dapat menulis prosedur atau fungsi sebagai hasil perhitungan; fungsi yang dihasilkan akan aman dan secepat (setelah dievaluasi) seperti bagian lain dari program yang baik dan ditulis dengan baik. Program yang mengandung exectidak aman tidak lebih berbahaya daripada program yang tidak aman melakukan kerusakan dengan sendirinya karena exectidak memberikan hak baru untuk program tersebut.
Thomas Baruchel
1
@ThomasBaruchel lagi, maksud saya adalah untuk melawan anggapan bahwa eval atau exec dapat dibuat aman. Secara khusus, jawaban ini mengklaim bahwa mengendalikan global dan penduduk setempat akan memungkinkan untuk menggunakannya dengan aman. Itu salah. Setiap kali Anda menggunakan exec dan eval, Anda harus tahu persis kode apa yang sedang dieksekusi. Jika tidak, maka Anda terbuka untuk operasi berbahaya.
Ned Batchelder
21

Ingatlah bahwa dari versi 3 execada fungsi!
jadi selalu gunakan exec(mystring)saja exec mystring.

bheks
sumber
11

eval()hanya untuk ekspresi, sementara eval('x+1')berfungsi, eval('x=1')tidak akan berfungsi misalnya. Dalam hal ini, lebih baik digunakan exec, atau bahkan lebih baik: cobalah untuk menemukan solusi yang lebih baik :)

LGB
sumber
11

Hindari execdaneval

Menggunakan execdan evaldengan Python sangat disukai.

Ada alternatif yang lebih baik

Dari jawaban teratas (beri penekanan pada saya):

Untuk pernyataan, gunakan exec.

Saat Anda membutuhkan nilai ekspresi, gunakan eval.

Namun, langkah pertama adalah bertanya pada diri sendiri apakah Anda benar-benar perlu. Kode yang dieksekusi umumnya harus menjadi posisi terakhir : Ini lambat, jelek dan berbahaya jika dapat berisi kode yang dimasukkan pengguna. Anda harus selalu melihat alternatif terlebih dahulu, seperti fungsi tingkat tinggi , untuk melihat apakah ini dapat lebih memenuhi kebutuhan Anda.

Dari Alternatif ke Eksekutif / Eval?

set dan dapatkan nilai variabel dengan nama dalam string

[Sementara eval] akan bekerja, umumnya tidak disarankan untuk menggunakan nama variabel yang mengandung arti untuk program itu sendiri.

Sebaliknya, lebih baik gunakan dict.

Itu tidak idiomatis

Dari http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (penekanan pada saya)

Python bukan PHP

Jangan mencoba untuk menghindari idiom Python karena beberapa bahasa lain melakukannya secara berbeda. Namespace berada dalam Python karena suatu alasan dan hanya karena memberi Anda alat execitu tidak berarti Anda harus menggunakan alat itu.

Itu berbahaya

Dari http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (penekanan pada saya)

Jadi eval tidak aman, bahkan jika Anda menghapus semua global dan builtin!

Masalah dengan semua upaya ini untuk melindungi eval () adalah bahwa mereka adalah daftar hitam . Mereka secara eksplisit menghapus hal-hal yang bisa berbahaya. Itu adalah pertempuran yang kalah karena jika hanya ada satu item yang tersisa dari daftar, Anda dapat menyerang sistem .

Jadi, bisakah eval dibuat aman? Sulit untuk dikatakan. Pada titik ini, tebakan terbaik saya adalah bahwa Anda tidak dapat membahayakan jika Anda tidak dapat menggunakan garis bawah ganda, jadi mungkin jika Anda mengecualikan string dengan garis bawah ganda, Anda aman. Mungkin...

Sulit dibaca dan dimengerti

Dari http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (penekanan tambang):

Pertama, execmempersulit manusia untuk membaca kode Anda . Untuk mengetahui apa yang terjadi, saya tidak hanya harus membaca kode Anda, saya harus membaca kode Anda, mencari tahu string apa yang akan dihasilkan, kemudian membaca kode virtual itu. Jadi, jika Anda bekerja di tim, atau menerbitkan perangkat lunak sumber terbuka, atau meminta bantuan di suatu tempat seperti StackOverflow, Anda mempersulit orang lain untuk membantu Anda. Dan jika ada kemungkinan Anda akan debugging atau memperluas kode ini 6 bulan dari sekarang, Anda membuatnya lebih sulit untuk Anda secara langsung.

Caridorc
sumber
"Tebakan terbaik saya adalah bahwa Anda tidak dapat melukai jika Anda tidak dapat menggunakan garis bawah ganda" - Anda dapat membuat string yang berisi garis bawah ganda, dan kemudian panggil eval string itu.
Stanley Bak
saran yang bagus, kecuali Anda menulis generator kode, atau pelari kerja atau serupa ... yang mungkin dilakukan kebanyakan orang di sini.
Erik Aronesty
Saya harus mengimpor path relatif dari file-config ( cfg.yaml):, reldir : ../my/dir/ dan reldir = cfg[reldir]. Namun, karena kode python ini akan berjalan pada Windows dan Linux, saya memerlukan ini untuk menyesuaikan dengan pembagi jalur sistem operasi yang berbeda; salah satu \\ atau /. Jadi saya gunakan reldir : os.path.join('..','my','dir')di file konfigurasi. Tapi ini hanya menghasilkan reldirstring literal ini, tidak dievaluasi, jadi saya tidak bisa membuka file reldir. Apakah anda punya saran?
Marie. P.
9

Anda menyelesaikan mengeksekusi kode menggunakan exec, seperti dengan sesi IDLE berikut:

>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']

4
gus
sumber
1
Ini tidak berfungsi dengan python normal. Setidaknya tidak dalam python 3.
Thomas Ahle
6

Seperti yang disebutkan lainnya, itu "exec" ..

tetapi, jika kode Anda mengandung variabel, Anda dapat menggunakan "global" untuk mengaksesnya, juga untuk mencegah kompiler meningkatkan kesalahan berikut:

NameError: nama 'p_variable' tidak ditentukan

exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)
Ghanem
sumber
5

Gunakan eval .

Pablo Santa Cruz
sumber
7
Eval () tidak menjalankan pernyataan.
RickyA
4

Perlu disebutkan, execsaudara itu ada juga dipanggil execfilejika Anda ingin memanggil file python. Itu kadang-kadang bagus jika Anda bekerja dalam paket pihak ketiga yang menyertakan IDE mengerikan dan Anda ingin kode di luar paket mereka.

Contoh:

execfile('/path/to/source.py)'

atau:

exec(open("/path/to/source.py").read())

pengguna1767754
sumber
3

Lihat eval :

x = 1
print eval('x+1')
->2
ryeguy
sumber
10
Eval () tidak menjalankan pernyataan.
RickyA
1

Saya mencoba beberapa hal, tetapi satu-satunya hal yang berhasil adalah sebagai berikut:

temp_dict = {}
exec("temp_dict['val'] = 10") 
print(temp_dict['val'])

keluaran:

10

paul-shuvo
sumber
0

Solusi paling logis adalah dengan menggunakan fungsi built-in eval () . Solusi lain adalah menulis string itu ke file python sementara dan menjalankannya.

John T
sumber
0

Ok .. Saya tahu ini bukan jawaban yang tepat, tetapi mungkin catatan untuk orang-orang yang melihat ini seperti saya. Saya ingin mengeksekusi kode khusus untuk pengguna / pelanggan yang berbeda tetapi juga ingin menghindari eksekutif / eval. Saya awalnya mencari menyimpan kode dalam database untuk setiap pengguna dan melakukan hal di atas.

Saya akhirnya membuat file pada sistem file dalam folder 'customer_filters' dan menggunakan modul 'imp', jika tidak ada filter yang diterapkan untuk pelanggan itu, itu hanya dijalankan pada

import imp


def get_customer_module(customerName='default', name='filter'):
    lm = None
    try:
        module_name = customerName+"_"+name;
        m = imp.find_module(module_name, ['customer_filters'])
        lm = imp.load_module(module_name, m[0], m[1], m[2])
    except:
        ''
        #ignore, if no module is found, 
    return lm

m = get_customer_module(customerName, "filter")
if m is not None:
    m.apply_address_filter(myobj)

jadi customerName = "jj" akan mengeksekusi apply_address_filter dari file customer_filters \ jj_filter.py

Brian
sumber
1
Bagaimana Anda mengelola keamanan? Bagaimana Anda tahu pelanggan tidak akan menyalahgunakan hak istimewa ini?
JSBach