ฉันจะรันสตริงที่มีรหัส Python ใน Python ได้อย่างไร


357

ฉันจะรันสตริงที่มีรหัส Python ใน Python ได้อย่างไร


5
เพื่อความยุติธรรมไม่เหมือนคำถามอื่น ๆ คำถามนี้ไม่ซ้ำกัน และคนอื่น ๆ ก็โพสต์คำถามพื้นฐานมากกว่านี้
DNS

8
แน่นอนคำตอบที่ถูกต้องมักจะ“ ไม่!”
bobince

23
@S Lott: ยังไม่ได้อ่านคำถามที่พบบ่อยเมื่อเร็ว ๆ นี้? "นอกจากนี้ยังเป็นการสมบูรณ์แบบที่จะถามและตอบคำถามการเขียนโปรแกรมของคุณเอง แต่แสร้งทำเป็นว่าคุณกำลังอยู่ใน Jeopardy: พูดในรูปแบบของคำถาม" นี่ไม่ใช่คำถามที่ไม่ดี +1
Devin Jeanpierre

49
@ S.Lott: คุณทำไม่ได้ คุณไม่จำเป็นต้อง หากคำถามไม่ได้อยู่ในเว็บไซต์แล้วก็เป็นเกมที่ยุติธรรม (ตามคำถามที่พบบ่อยตามที่ระบุไว้แล้ว) เพียงตอบทุกคำถามราวกับว่า OP ต้องการความช่วยเหลือ พวกเขาอาจไม่ได้ แต่คนต่อไปที่อ่านคำถามของพวกเขาอาจจะ แค่ 2 เซ็นต์ของฉัน
บิลจิ้งจก

1
สำหรับทุกสิ่งที่คุณบอกว่าไม่ต้องทำ: ฉันมีคดีที่ฉันต้องการอย่างยิ่ง มันเกี่ยวข้องกับการประมวลผลแบบกระจาย
sudo

คำตอบ:


331

สำหรับข้อความสั่งให้ใช้exec(string)(Python 2/3) หรือexec string(Python 2):

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

เมื่อคุณต้องการค่าของนิพจน์ให้ใช้eval(string):

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

อย่างไรก็ตามขั้นตอนแรกควรถามตัวเองว่าคุณต้องการจริงๆหรือไม่ โดยทั่วไปการเรียกใช้โค้ดควรเป็นตำแหน่งสุดท้าย: มันช้าน่าเกลียดและอันตรายหากสามารถมีรหัสที่ผู้ใช้ป้อนได้ คุณควรพิจารณาทางเลือกอื่นก่อนเช่นฟังก์ชั่นการสั่งซื้อที่สูงขึ้นเพื่อดูว่าสิ่งเหล่านี้สามารถตอบสนองความต้องการของคุณได้ดีขึ้นหรือไม่


1
แต่ขอบเขตของโค้ดที่ดำเนินการโดย 'exec' นั้นเป็นอย่างไร ซ้อนกันไหม
jondinham

21
กรณีทั่วไปที่คนต้องการที่จะใช้ 'exec' เป็นสิ่งที่ต้องการif s=='foo': x.foo = 42 elif s=='bar': x.bar = 42ฯลฯ exec ("x.%s = 42" % s)ซึ่งพวกเขาก็อาจจะเขียนเป็น สำหรับกรณีทั่วไปนี้ (ที่คุณเพียงต้องการเข้าถึงคุณลักษณะของวัตถุที่เก็บไว้ในสตริง) มีฟังก์ชั่นที่เร็วกว่าสะอาดกว่าและปลอดภัยกว่าgetattrเพียงแค่เขียนgetattr(x, s) = 42ให้มีความหมายเหมือนกัน
ShreevatsaR

5
exec ช้ากว่าล่าม python อย่างไร?
Cris Stringfellow

6
@ShreevatsaR คุณไม่ได้หมายถึงsetattr(x, s, 42)เหรอ? ฉันลองgetattr(x, 2) = 42แล้วมันล้มเหลวด้วยcan't assign to function call: <string>, line 1
แทนเนอร์เซมาราด

6
@ แทนเนอร์: อืม ใช่แน่นอนsetattr(x, s, 42)คือไวยากรณ์ที่ถูกต้อง แปลกใจที่มันใช้เวลานานมากสำหรับข้อผิดพลาดที่จะถูกจับ อย่างไรก็ตามประเด็นก็คือgetattrและsetattrเป็นทางเลือกexecเมื่อทุกสิ่งที่คุณต้องการคือการได้รับสมาชิกโดยพลการค้นหาสตริง
ShreevatsaR

68

ในตัวอย่างสตริงจะถูกดำเนินการเป็นรหัสโดยใช้ฟังก์ชั่น 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()

1
การสลับ stdout และ stderr แบบนั้นทำให้ฉันกังวลมาก ดูเหมือนว่าอาจทำให้เกิดปัญหาด้านความปลอดภัยขนาดใหญ่ มีวิธีแก้ไขไหม?
Narcolapser

1
@Narcolapser คุณควรจะกังวลกับการใช้execเลย (เว้นแต่คุณจะรู้ว่าสตริงโค้ดนั้นมาจากแหล่งที่เชื่อถือได้)
bruno desthuilliers

27

evalและexecเป็นทางออกที่ถูกต้องและสามารถใช้ในที่ปลอดภัยยิ่งขึ้นลักษณะ

ตามที่กล่าวไว้ในคู่มืออ้างอิงของ Pythonและอธิบายอย่างชัดเจนในบทช่วยสอนนี้evalและexecฟังก์ชั่นใช้พารามิเตอร์พิเศษสองตัวที่อนุญาตให้ผู้ใช้ระบุฟังก์ชันและตัวแปรระดับโลกและระดับท้องถิ่น

ตัวอย่างเช่น:

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

ในสาระสำคัญคุณจะกำหนด namespace ที่รหัสจะถูกดำเนินการ


9
มันเป็นไปไม่ได้ที่จะทำให้ปลอดภัย EVAL: Eval จริงๆเป็นอันตราย หากคุณใช้รหัสจากฉันและประเมินผลฉันสามารถแบ่งโปรแกรม Python ของคุณออกได้ จบเกม.
Ned Batchelder

3
@ v.oddou ฉันตอบสนองต่อคำสั่งของ alan "eval และ exec .. สามารถใช้ได้อย่างปลอดภัย" นี่เป็นเท็จ หากมีคนพูดว่า "ทุบตีสามารถใช้ในลักษณะที่ปลอดภัย" ที่จะเป็นเท็จ ทุบตีเป็นอันตราย มันเป็นอันตรายที่จำเป็น แต่ก็ยังมีอันตราย การอ้างว่าการพิสูจน์สามารถทำให้ปลอดภัยนั้นเป็นเรื่องผิด
เน็ดแบทเชลเดอร์

1
@NedBatchelder ใช่แน่นอน และลิงค์ที่คุณชี้ไปนั้นเป็นวัสดุที่ดี ด้วยพลังที่มาพร้อมกับความรับผิดชอบดังนั้นประเด็นก็คือให้ตระหนักถึงพลังที่อาจเกิดขึ้นของการประเมินผล และถ้าเราตัดสินใจว่าพลัง = อันตราย
v.oddou

3
@NedBatchelder โค้ดหลาย ๆ ชิ้นที่เขียนด้วย Python อาจเป็นอันตรายได้เช่นกัน แต่ทำไมคุณคิดว่าevalหรือexecตั้งใจจะใช้เป็นexec(input("Type what you want"))? มีหลายกรณีที่โปรแกรมอาจเขียนโพรซีเดอร์หรือฟังก์ชันอันเป็นผลมาจากการคำนวณ ฟังก์ชั่นที่ได้จะมีความปลอดภัยและรวดเร็ว (ประเมินผลครั้งเดียว) เป็นส่วนอื่น ๆ ของโปรแกรมที่ดีและเป็นลายลักษณ์อักษร โปรแกรมที่ไม่ปลอดภัยที่มีexecอันตรายไม่มากไปกว่าโปรแกรมที่ไม่ปลอดภัยซึ่งสร้างความเสียหายด้วยตัวเองเนื่องจากexecไม่ได้ให้สิทธิพิเศษใหม่แก่โปรแกรม
โทมัสบารูเชล

1
@ThomasBaruchel อีกครั้งจุดของฉันคือการต่อต้านความคิดที่ว่า eval หรือ exec สามารถทำให้ปลอดภัย โดยเฉพาะอย่างยิ่งคำตอบนี้อ้างว่าการควบคุมกลมและคนในท้องถิ่นจะทำให้สามารถใช้งานได้อย่างปลอดภัย นั่นเป็นเท็จ เมื่อใดก็ตามที่คุณใช้ exec และ eval คุณจะต้องรู้ว่าโค้ดใดที่ถูกเรียกใช้ ถ้าคุณทำไม่ได้แสดงว่าคุณเปิดรับการปฏิบัติการที่อันตราย
Ned Batchelder

21

โปรดจำไว้ว่าจากเวอร์ชั่น 3 execเป็นฟังก์ชั่น!
จึงมักใช้แทนexec(mystring)exec mystring


11

eval()เป็นเพียงสำหรับการแสดงออกในขณะที่eval('x+1')ทำงานeval('x=1')จะไม่ทำงานเช่น ในกรณีนี้ควรใช้execหรือดีกว่า: ลองค้นหาวิธีแก้ปัญหาที่ดีกว่า :)


11

หลีกเลี่ยงexecและeval

การใช้งานexecและevalใน Python นั้นทำให้ขมวดคิ้วอย่างมาก

มีทางเลือกที่ดีกว่า

จากคำตอบยอดนิยม (เน้นการเน้นของฉัน):

execสำหรับงบการใช้งาน

evalเมื่อคุณต้องการความคุ้มค่าของการแสดงออกการใช้

อย่างไรก็ตามขั้นตอนแรกควรถามตัวเองว่าคุณต้องการจริงๆหรือไม่ โดยทั่วไปรหัสการดำเนินการควรเป็นตำแหน่งสุดท้าย : มันช้าน่าเกลียดและอันตรายหากสามารถมีรหัสที่ผู้ใช้ป้อนได้ คุณควรพิจารณาทางเลือกอื่นก่อนเช่นฟังก์ชั่นการสั่งซื้อที่สูงขึ้นเพื่อดูว่าสิ่งเหล่านี้สามารถตอบสนองความต้องการของคุณได้ดีขึ้นหรือไม่

จากทางเลือกสู่ exec / eval?

กำหนดและรับค่าของตัวแปรด้วยชื่อในสตริง

[ขณะeval] จะใช้งานได้โดยทั่วไปจะไม่แนะนำให้ใช้ชื่อตัวแปรที่มีความหมายกับโปรแกรมเอง

ให้ใช้ dict แทน

มันไม่ใช่สำนวน

จากhttp://lucumr.pocoo.org/2011/2/1/exec-in-python/ (เน้นที่เหมือง)

Python ไม่ใช่ PHP

อย่าพยายามหลีกเลี่ยงสำนวน Python เพราะภาษาอื่นบางภาษาใช้มันแตกต่างกัน เนมสเปซอยู่ใน Python ด้วยเหตุผลเพียงเพราะมันให้เครื่องมือexecมันไม่ได้หมายความว่าคุณควรใช้เครื่องมือนั้น

มันอันตราย

จากhttp://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (เน้นที่เหมือง)

ดังนั้น Eval จึงไม่ปลอดภัยแม้ว่าคุณจะลบรูปทรงกลมทั้งหมดและตัวในออก!

ปัญหาที่มีทั้งหมดของเหล่านี้พยายามที่จะป้องกัน EVAL () ที่พวกเขามีบัญชีดำ พวกเขาลบสิ่งที่อาจเป็นอันตรายได้อย่างชัดเจน นั่นคือการต่อสู้ที่พ่ายแพ้เพราะหากมีเพียงหนึ่งไอเท็มที่เหลืออยู่ในรายการคุณสามารถโจมตีระบบได้ได้

ดังนั้น eval สามารถทำให้ปลอดภัยหรือไม่ ยากที่จะพูด. ณ จุดนี้การคาดเดาที่ดีที่สุดของฉันคือคุณไม่สามารถทำอันตรายใด ๆ ได้หากคุณไม่สามารถใช้เครื่องหมายขีดล่างคู่ใด ๆ ได้ดังนั้นหากคุณไม่รวมสตริงใด ๆ ที่มีการขีดเส้นใต้สองเท่าคุณจะปลอดภัย อาจจะ...

อ่านและเข้าใจได้ยาก

จากhttp://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (เน้นที่เหมือง):

ก่อนอื่นexecทำให้มนุษย์อ่านรหัสของคุณยากขึ้น เพื่อที่จะทราบว่าเกิดอะไรขึ้นฉันไม่เพียงแค่ต้องอ่านรหัสของคุณฉันต้องอ่านรหัสของคุณคิดออกว่าจะสร้างสตริงอะไรจากนั้นอ่านรหัสเสมือนนั้น ดังนั้นหากคุณกำลังทำงานเป็นทีมหรือเผยแพร่ซอฟต์แวร์โอเพนซอร์ซหรือขอความช่วยเหลือจากที่ไหนสักแห่งเช่น StackOverflow คุณจะทำให้คนอื่นยากที่จะช่วยเหลือคุณ และถ้ามีโอกาสที่คุณจะทำการดีบั๊กหรือขยายโค้ดนี้อีก 6 เดือนนับจากนี้คุณจะทำให้มันยากขึ้นสำหรับตัวคุณเองโดยตรง


"เดาที่ดีที่สุดของฉันคือคุณไม่สามารถทำอันตรายใด ๆ หากคุณไม่สามารถใช้ขีดเส้นใต้คู่ใด ๆ " - คุณสามารถสร้างสตริงที่มีขีดล่างคู่แล้วโทร eval สายนั้น
Stanley Bak

คำแนะนำที่ดีเว้นแต่คุณจะเขียนโปรแกรมสร้างรหัสหรือนักวิ่งหรือผู้ที่คล้ายกัน ... ซึ่งคนส่วนใหญ่ที่นี่อาจจะทำ
Erik Aronesty

ฉันมีที่จะนำเข้าทางญาติจากการตั้งค่าไฟล์ ( cfg.yaml) และreldir : ../my/dir/ reldir = cfg[reldir]อย่างไรก็ตามเนื่องจากโค้ดไพ ธ อนนี้จะทำงานทั้งบน Windows และ Linux ฉันต้องการสิ่งนี้เพื่อปรับให้เข้ากับตัวแบ่งพา ธ ของระบบปฏิบัติการที่แตกต่างกัน อย่างใดอย่างหนึ่งหรือ\\ /ดังนั้นฉันจะใช้reldir : os.path.join('..','my','dir')ในไฟล์กำหนดค่า แต่ตอนนี้ผลเฉพาะในการเป็นนี้สตริงตัวอักษรที่ไม่ถูกประเมินดังนั้นฉันไม่สามารถเปิดไฟล์ในreldir reldirคุณมีข้อเสนอแนะหรือไม่?
มารี P.

9

คุณดำเนินการโค้ดให้สำเร็จโดยใช้ exec เช่นเดียวกับเซสชัน IDLE ต่อไปนี้:

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

4

1
สิ่งนี้ไม่ทำงานในงูหลามปกติ อย่างน้อยไม่ได้อยู่ในหลาม 3
โทมัส Ahle

6

ดังที่คนอื่น ๆ พูดถึงมันคือ "exec" ..

แต่ในกรณีที่โค้ดของคุณมีตัวแปรคุณสามารถใช้ "global" เพื่อเข้าถึงมันได้เช่นกันเพื่อป้องกันคอมไพเลอร์เพื่อเพิ่มข้อผิดพลาดดังต่อไปนี้:

NameError: ชื่อ 'p_variable' ไม่ได้ถูกกำหนดไว้

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


4

เป็นมูลค่าการกล่าวขวัญว่าexecพี่ชายของอยู่ที่เรียกว่าexecfileถ้าคุณต้องการเรียกไฟล์หลาม บางครั้งก็เป็นสิ่งที่ดีหากคุณทำงานในแพ็คเกจของบุคคลที่สามซึ่งมี IDE อยู่ในตัวและคุณต้องการโค้ดนอกแพ็คเกจของพวกเขา

ตัวอย่าง:

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

หรือ:

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



1

ฉันพยายามทำบางสิ่งเล็กน้อย แต่สิ่งเดียวที่ใช้ได้คือ:

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

เอาท์พุท:

10


0

วิธีการแก้ปัญหาเชิงตรรกะมากที่สุดคือการใช้ฟังก์ชั่นeval () ในตัวโซลูชั่นอื่น ๆ คือการเขียนสตริงนั้นไปยังไฟล์ไพ ธ อนชั่วคราวและดำเนินการ


0

ตกลง .. ฉันรู้ว่านี่ไม่ใช่คำตอบที่แน่ชัด แต่อาจเป็นเพราะคนที่มองสิ่งนี้เหมือนฉัน ฉันต้องการรันโค้ดเฉพาะสำหรับผู้ใช้ / ลูกค้าที่แตกต่างกัน แต่ยังต้องการหลีกเลี่ยง exec / eval ตอนแรกฉันพยายามเก็บรหัสไว้ในฐานข้อมูลสำหรับผู้ใช้แต่ละคนและทำตามขั้นตอนด้านบน

ฉันลงเอยด้วยการสร้างไฟล์ในระบบไฟล์ภายในโฟลเดอร์ 'customer_filters' และใช้โมดูล 'imp' หากไม่มีตัวกรองสำหรับลูกค้ารายนั้นมันเพิ่งจะดำเนินการ

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)

ดังนั้น customerName = "jj" จะดำเนินการ Apply_address_filter จากไฟล์ customer_filters \ jj_filter.py


1
คุณจัดการความปลอดภัยได้อย่างไร คุณจะรู้ได้อย่างไรว่าลูกค้าจะไม่ใช้สิทธิ์นี้ในทางที่ผิด
JSBach
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.