Eval Python () ทำอะไรได้บ้าง


306

ในหนังสือที่ฉันอ่านบน Python มันจะใช้รหัสต่อไป eval(input('blah'))

ฉันอ่านเอกสารและเข้าใจ แต่ก็ยังไม่เห็นว่ามันเปลี่ยนinput()ฟังก์ชั่นอย่างไร

มันทำอะไร? มีคนอธิบายได้ไหม


4
ฟังก์ชัน Eval พยายามเรียกใช้งานและตีความสตริง (อาร์กิวเมนต์) ที่ส่งไปเป็นรหัสไพ ธ อน x = 1 print (eval ('x + 1')) ผลลัพธ์ของโค้ดข้างต้นจะเป็น 2 ข้อเสียของวิธีการดังกล่าวคือผู้ใช้จะได้รับความเป็นอิสระในการเขียนโค้ดซึ่งอาจส่งผลให้เกิดความเสียหายได้ การเข้าถึงตัวแปรและวิธีการมากมายโดยผ่านพารามิเตอร์ส่วนกลางและท้องถิ่นในฟังก์ชัน eval
ATIF IBAD KHAN

คำตอบ:


276

ฟังก์ชั่น eval อนุญาตให้โปรแกรม Python รันโค้ด Python ภายในตัวมันเอง

ตัวอย่าง eval (เปลือกโต้ตอบ):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1

25
ฮ่าฮ่านั่นเป็นตัวอย่างเล็กน้อย แต่คุณสามารถให้ผู้ใช้พิมพ์ในคำสั่งโดยพลการและให้ไพ ธ อนรันมัน ดังนั้นคุณสามารถมีประเภทผู้ใช้ในสตริงคำสั่งแล้วมีงูใหญ่เรียกใช้เป็นรหัส ตัวอย่างเช่น: eval ("__ import __ ('os'). remove ('file')")
BYS2

60
มันจะดูไร้ประโยชน์จนกว่าคุณจะพบความต้องการ มันถูกใช้บนเว็บไซต์เช่น codepad.org เพื่อให้คุณสามารถเรียกใช้สคริปต์ในสภาพแวดล้อมการทดสอบ eval()ยังสามารถใช้เพื่อเรียกใช้งานโค้ดแบบไดนามิกสูง แต่คุณควรตระหนักถึงความเสี่ยงด้านความปลอดภัยและประสิทธิภาพก่อนที่จะใช้งาน
George Cummins

6
@GeorgeCummins, codepad.org ไม่ได้ใช้หรือมันอาจจะทำในสิ่งที่มันจะมีeval eval
Mike Graham

16
@GeorgeCummins: codepag.org ทำงานทุกอย่างในแซนด์บ็อกซ์: คุก chroot ที่มีการตรวจสอบ ptrace ในเครื่องเสมือนเพื่อป้องกันไม่ให้โค้ดที่เป็นอันตรายทำสิ่งที่ไม่ดี มีความซับซ้อนมากขึ้นกว่า eval ธรรมดา นอกจากนี้ Eval ยังเป็นแบบ Python codepad รองรับเครือภาษาต่างๆ
FogleBird

4
@ GeorgeCummins, codepad ทำงานเป็นระบบที่ซับซ้อนมากในการรันโปรแกรมโดยพลการ evalนอกเหนือจากการไม่ปลอดภัยไม่สามารถเรียกใช้โปรแกรมทั้งหมดเช่นเดียวกับแผ่นจดบันทึกเพราะมันสามารถประเมินการแสดงออกเพียงครั้งเดียว
Mike Graham

165

eval()ตีความสตริงเป็นรหัส เหตุผลที่หลายคนเตือนคุณเกี่ยวกับการใช้สิ่งนี้คือเนื่องจากผู้ใช้สามารถใช้สิ่งนี้เป็นตัวเลือกในการเรียกใช้รหัสบนคอมพิวเตอร์ หากคุณมีeval(input())และosนำเข้าบุคคลสามารถพิมพ์ไฟล์input() os.system('rm -R *')ที่จะลบไฟล์ทั้งหมดในโฮมไดเร็กตอรี่ของคุณ (สมมติว่าคุณมีระบบยูนิกซ์) การใช้eval()เป็นช่องโหว่ความปลอดภัย หากคุณต้องการแปลงสตริงเป็นรูปแบบอื่นลองใช้สิ่งที่ทำเช่นint()นั้น


14
คุณหมายถึงการใช้evalกับinput()เป็นช่องโหว่ความปลอดภัย อย่าใส่input()คำแถลงที่เป็นเท็จและคุณจะไม่เป็นอะไร
Rohmer

19
@Rohmer ข้อมูลที่ไม่ปลอดภัยสามารถมาจากทุกที่: คำขอจากเว็บ, ช่องป้อนข้อมูลในรูปแบบ, อ่านไฟล์, ... ไม่เพียง แต่จากคอนโซลอินพุต แม้ว่าคุณจะเขียนไฟล์เอง แต่ก็ยังสามารถมีอินพุตที่มาจากแหล่งที่ไม่น่าเชื่อถือ ดังนั้นevalปัญหาด้านความปลอดภัยในหลายกรณี
sanderd17

3
เนื่องจากinputมักจะใช้เวลาข้อมูลจากคอนโซลผู้ใช้ก็สามารถออกจากโปรแกรมและพิมพ์rm -R *ต่อไป ...
CZ

63

คำตอบที่ดีมากมายที่นี่ แต่ไม่มีใครอธิบายถึงการใช้งานeval()ในบริบทของมันglobalsและlocalskwargs เช่นeval(expression, globals=None, locals=None)(ดูเอกสารสำหรับeval ที่นี่ )

เหล่านี้สามารถใช้เพื่อ จำกัด ฟังก์ชั่นที่มีอยู่ผ่านevalฟังก์ชั่น ตัวอย่างเช่นหากคุณโหลดล่ามไพ ธ อนขึ้นใหม่locals()และglobals()จะเหมือนกันและมีลักษณะดังนี้:

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

มีฟังก์ชั่นภายในbuiltinsโมดูลที่สามารถทำลายระบบได้อย่างมาก แต่มันเป็นไปได้ที่จะบล็อกทุกอย่างที่เราไม่ต้องการ ลองยกตัวอย่าง สมมติว่าเราต้องการสร้างรายการเพื่อแสดงโดเมนของคอร์ที่มีอยู่ในระบบ สำหรับฉันฉันมี 8 [1, 8]แกนดังนั้นฉันต้องการรายการ

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

ในทำนองเดียวกันทั้งหมดที่__builtins__มีอยู่

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

ตกลง. ดังนั้นเราจึงเห็นฟังก์ชั่นหนึ่งที่เราต้องการให้เปิดเผยและตัวอย่างของวิธีหนึ่ง (ของหลาย ๆ ที่สามารถซับซ้อนกว่า) วิธีที่เราไม่ต้องการสัมผัส ลองปิดกั้นทุกอย่าง

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

เราได้ปิดกั้น__builtins__ฟังก์ชั่นทั้งหมดอย่างมีประสิทธิภาพและนำมาซึ่งระดับการป้องกันในระบบของเรา ณ จุดนี้เราสามารถเริ่มเพิ่มกลับเข้าไปในฟังก์ชั่นที่เราต้องการสัมผัส

>>>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

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

NB

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

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

29

ใน Python 2.x input(...)เทียบเท่ากับeval(raw_input(...))ใน Python 3.x raw_inputถูกเปลี่ยนชื่อinputซึ่งฉันสงสัยว่าจะนำไปสู่ความสับสนของคุณ (คุณอาจดูเอกสารประกอบinputใน Python 2.x) นอกจากนี้eval(input(...))จะทำงานได้ดีใน Python 3.x แต่จะเพิ่มTypeErrorใน Python 2

ในกรณีevalนี้ใช้เพื่อบีบบังคับสตริงที่ส่งคืนจากinputนิพจน์และตีความ โดยทั่วไปถือว่าเป็นการปฏิบัติที่ไม่ดี


หรือเป็นหนังสือ Python 3.x ซึ่งinputแปลว่าอะไรraw_inputใน 2.x
dan04

1
ใช่ที่เกิดขึ้นกับฉันหลังจากที่ฉันเขียนคำตอบเริ่มต้นของฉันและนั่นเป็นกรณีที่ชัดเจน
zeekay

6

อาจเป็นตัวอย่างที่ทำให้เข้าใจผิดในการอ่านบรรทัดและตีความมัน

ลองeval(input())และประเภท"1+1"- 2นี้ควรพิมพ์ Eval ประเมินค่านิพจน์


ทำไมฉันควรพิมพ์ระหว่างคำพูด? อินพุตกำลังรับสตริงและส่งผ่านไปยัง eval ไม่ได้รันโค้ดดังนั้นฉันควรจะดีถ้าฉันพิมพ์ 1 + 1 ... ¿?
JC Rocamonde

สิ่งนี้คือคุณกำลังผสม P2.x และ 3.x ใน Python 2 โค้ดของคุณใช้งานได้ แต่มันก็ไม่สมเหตุสมผลที่จะทำ eval สองครั้ง ในหลาม 3 มันไม่ได้และส่งกลับสตริง
JC Rocamonde

6

eval()ประเมินสตริงที่ส่งผ่านเป็นนิพจน์ Python และส่งคืนผลลัพธ์ ตัวอย่างเช่นeval("1 + 1")ตีความและดำเนินการแสดงออก"1 + 1"และส่งกลับผลลัพธ์ (2)

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

อ่านเพิ่มเติมเกี่ยวกับ EVAL ที่นี่


6

eval()ตามชื่อที่แนะนำประเมินอาร์กิวเมนต์ที่ส่งผ่าน

raw_input()ขณะนี้input()อยู่ในเวอร์ชัน python 3.x ตัวอย่างที่พบมากที่สุดสำหรับการใช้งานeval()คือการใช้เพื่อให้ฟังก์ชั่นที่input()ให้ไว้ในหลามรุ่น 2.x raw_input ส่งคืนข้อมูลที่ผู้ใช้ป้อนเป็นสตริงขณะที่อินพุตทำการประเมินค่าของข้อมูลที่ป้อนและส่งคืน

eval(input("bla bla"))ดังนั้นจึงทำซ้ำการทำงานของinput()ใน 2.x คือการประเมินข้อมูลที่ผู้ใช้ป้อน

ในระยะสั้น: eval()ประเมินข้อโต้แย้งที่ส่งผ่านไปและมันeval('1 + 1')กลับมา 2


6

แอปพลิเคชั่นที่มีประโยชน์อย่างหนึ่งของeval()คือการประเมินการแสดงออกของหลามจากสตริง ตัวอย่างเช่นโหลดจากการแสดงสตริงไฟล์ของพจนานุกรม:

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

อ่านมันออกมาเป็นตัวแปรแล้วแก้ไขมัน:

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

เอาท์พุท:

{'Greeting': 'Hello world'}

7
นี่จะตอบคำถามที่ถามว่าevalทำอะไรได้บ้าง?
jkd

4

ฉันมาสายเพื่อตอบคำถามนี้ แต่ดูเหมือนว่าไม่มีใครตอบคำถามได้ชัดเจน

หากผู้ใช้ป้อนค่าตัวเลขinput()จะส่งคืนสตริง

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

ดังนั้นeval()จะประเมินค่าที่ส่งคืน (หรือนิพจน์) ซึ่งเป็นสตริงและส่งคืนจำนวนเต็ม / จำนวนเต็ม

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

ของแหล่งที่มานี่คือการปฏิบัติที่ไม่ดี int()หรือfloat()ควรใช้แทนeval()ในกรณีนี้

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14

3

อีกตัวเลือกหนึ่งถ้าคุณต้องการที่จะ จำกัด ast.literal_eval()สตริงการประเมินผลเพื่อตัวอักษรที่ง่ายคือการใช้งาน ตัวอย่างบางส่วน:

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}

จากเอกสาร :

ประเมินโหนดการแสดงออกหรือสตริงที่มีการแสดงตัวอักษรหรือคอนเทนเนอร์ของ Python อย่างปลอดภัย สตริงหรือโหนดที่ให้ไว้อาจเท่านั้นประกอบด้วยโครงสร้างตัวอักษร Python ดังต่อไปนี้ : สตริง, ไบต์, ตัวเลข, สิ่งอันดับ, รายการ, dicts, ชุด, booleans และไม่มี

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

สำหรับสาเหตุที่ จำกัด ดังนั้นจากรายการส่งเมล์ :

การอนุญาตให้ใช้ตัวดำเนินการนิพจน์ที่มีตัวอักษรเป็นไปได้ แต่มีความซับซ้อนมากกว่าการใช้งานในปัจจุบัน การใช้งานที่ง่ายไม่ปลอดภัย: คุณสามารถชักนำให้เกิดการใช้งาน CPU และหน่วยความจำที่ไม่มีขอบเขตได้โดยไม่ต้องพยายาม (ลอง "9 ** 9 ** 9" หรือ "[ไม่มี] * 9 ** 9")

สำหรับประโยชน์ของฟังก์ชั่นนี้มีประโยชน์ในการ "อ่านย้อนกลับ" ค่าตามตัวอักษรและภาชนะบรรจุที่เป็นสตริงโดย repr () ตัวอย่างนี้สามารถใช้สำหรับการทำให้เป็นอนุกรมในรูปแบบที่คล้ายกับ แต่มีประสิทธิภาพมากกว่า JSON


1
ast.literal_eval ไม่รองรับผู้ให้บริการซึ่งตรงกันข้ามกับของคุณ '1+1'ตัวอย่างอย่างไรก็ตามมันรองรับรายการตัวเลขสตริง ฯลฯ และเป็นทางเลือกที่ดีสำหรับevalกรณีใช้งานทั่วไป
benjimin

@benjimin โอ้คุณพูดถูก - มันเป็นเรื่องแปลกประหลาดที่ยอมรับ 1 + 1! stackoverflow.com/questions/40584417/…
Brian Burns
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.