ในหนังสือที่ฉันอ่านบน Python มันจะใช้รหัสต่อไป eval(input('blah'))
ฉันอ่านเอกสารและเข้าใจ แต่ก็ยังไม่เห็นว่ามันเปลี่ยนinput()
ฟังก์ชั่นอย่างไร
มันทำอะไร? มีคนอธิบายได้ไหม
ในหนังสือที่ฉันอ่านบน Python มันจะใช้รหัสต่อไป eval(input('blah'))
ฉันอ่านเอกสารและเข้าใจ แต่ก็ยังไม่เห็นว่ามันเปลี่ยนinput()
ฟังก์ชั่นอย่างไร
มันทำอะไร? มีคนอธิบายได้ไหม
คำตอบ:
ฟังก์ชั่น eval อนุญาตให้โปรแกรม Python รันโค้ด Python ภายในตัวมันเอง
ตัวอย่าง eval (เปลือกโต้ตอบ):
>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
eval()
ยังสามารถใช้เพื่อเรียกใช้งานโค้ดแบบไดนามิกสูง แต่คุณควรตระหนักถึงความเสี่ยงด้านความปลอดภัยและประสิทธิภาพก่อนที่จะใช้งาน
eval
eval
eval
นอกเหนือจากการไม่ปลอดภัยไม่สามารถเรียกใช้โปรแกรมทั้งหมดเช่นเดียวกับแผ่นจดบันทึกเพราะมันสามารถประเมินการแสดงออกเพียงครั้งเดียว
eval()
ตีความสตริงเป็นรหัส เหตุผลที่หลายคนเตือนคุณเกี่ยวกับการใช้สิ่งนี้คือเนื่องจากผู้ใช้สามารถใช้สิ่งนี้เป็นตัวเลือกในการเรียกใช้รหัสบนคอมพิวเตอร์ หากคุณมีeval(input())
และos
นำเข้าบุคคลสามารถพิมพ์ไฟล์input()
os.system('rm -R *')
ที่จะลบไฟล์ทั้งหมดในโฮมไดเร็กตอรี่ของคุณ (สมมติว่าคุณมีระบบยูนิกซ์) การใช้eval()
เป็นช่องโหว่ความปลอดภัย หากคุณต้องการแปลงสตริงเป็นรูปแบบอื่นลองใช้สิ่งที่ทำเช่นint()
นั้น
eval
กับinput()
เป็นช่องโหว่ความปลอดภัย อย่าใส่input()
คำแถลงที่เป็นเท็จและคุณจะไม่เป็นอะไร
eval
ปัญหาด้านความปลอดภัยในหลายกรณี
input
มักจะใช้เวลาข้อมูลจากคอนโซลผู้ใช้ก็สามารถออกจากโปรแกรมและพิมพ์rm -R *
ต่อไป ...
คำตอบที่ดีมากมายที่นี่ แต่ไม่มีใครอธิบายถึงการใช้งานeval()
ในบริบทของมันglobals
และlocals
kwargs เช่น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]
ใน 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
นิพจน์และตีความ โดยทั่วไปถือว่าเป็นการปฏิบัติที่ไม่ดี
input
แปลว่าอะไรraw_input
ใน 2.x
อาจเป็นตัวอย่างที่ทำให้เข้าใจผิดในการอ่านบรรทัดและตีความมัน
ลองeval(input())
และประเภท"1+1"
- 2
นี้ควรพิมพ์ Eval ประเมินค่านิพจน์
eval()
ประเมินสตริงที่ส่งผ่านเป็นนิพจน์ Python และส่งคืนผลลัพธ์ ตัวอย่างเช่นeval("1 + 1")
ตีความและดำเนินการแสดงออก"1 + 1"
และส่งกลับผลลัพธ์ (2)
เหตุผลหนึ่งที่คุณอาจสับสนคือเพราะรหัสที่คุณอ้างถึงเกี่ยวข้องกับระดับการอ้อม การเรียกใช้ฟังก์ชั่นด้านใน (อินพุต) ได้รับการดำเนินการก่อนเพื่อให้ผู้ใช้เห็นพรอมต์ "blah" สมมติว่าพวกเขาตอบสนองด้วย "1 + 1" (เพิ่มเครื่องหมายคำพูดเพื่อความชัดเจนอย่าพิมพ์เมื่อเรียกใช้โปรแกรมของคุณ) ฟังก์ชันอินพุตจะส่งคืนสตริงนั้นซึ่งจะถูกส่งผ่านไปยังฟังก์ชันด้านนอก (eval) ซึ่งตีความสตริงและ ส่งคืนผลลัพธ์ (2)
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
แอปพลิเคชั่นที่มีประโยชน์อย่างหนึ่งของ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'}
eval
ทำอะไรได้บ้าง?
ฉันมาสายเพื่อตอบคำถามนี้ แต่ดูเหมือนว่าไม่มีใครตอบคำถามได้ชัดเจน
หากผู้ใช้ป้อนค่าตัวเลข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
อีกตัวเลือกหนึ่งถ้าคุณต้องการที่จะ จำกัด 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
ast.literal_eval
ไม่รองรับผู้ให้บริการซึ่งตรงกันข้ามกับของคุณ '1+1'
ตัวอย่างอย่างไรก็ตามมันรองรับรายการตัวเลขสตริง ฯลฯ และเป็นทางเลือกที่ดีสำหรับeval
กรณีใช้งานทั่วไป