Trong cuốn sách mà tôi đang đọc trên Python, nó tiếp tục sử dụng mã eval(input('blah'))
Tôi đọc tài liệu và tôi hiểu nó, nhưng tôi vẫn không thấy nó thay đổi input()
chức năng như thế nào .
Nó làm gì? Ai đó có thể giải thích?
Trong cuốn sách mà tôi đang đọc trên Python, nó tiếp tục sử dụng mã eval(input('blah'))
Tôi đọc tài liệu và tôi hiểu nó, nhưng tôi vẫn không thấy nó thay đổi input()
chức năng như thế nào .
Nó làm gì? Ai đó có thể giải thích?
Câu trả lời:
Hàm eval cho phép một chương trình Python chạy mã Python trong chính nó.
ví dụ eval (vỏ tương tác):
>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
eval()
cũng có thể được sử dụng để thực thi mã rất năng động, nhưng bạn nên tự nhận thức đầy đủ về các rủi ro bảo mật và hiệu suất trước khi sử dụng nó.
eval
, cũng không thể làm những gì nó làm với eval
.
eval
, ngoài việc không an toàn, không thể chạy toàn bộ chương trình như codepad vì nó chỉ có thể đánh giá một biểu thức duy nhất.
eval()
diễn giải một chuỗi dưới dạng mã. Lý do tại sao rất nhiều người đã cảnh báo bạn về việc sử dụng điều này là bởi vì người dùng có thể sử dụng điều này như một tùy chọn để chạy mã trên máy tính. Nếu bạn có eval(input())
và os
nhập, một người có thể nhập vào input()
os.system('rm -R *')
đó sẽ xóa tất cả các tệp của bạn trong thư mục chính của bạn. (Giả sử bạn có một hệ thống unix). Sử dụng eval()
là một lỗ hổng bảo mật. Nếu bạn cần chuyển đổi chuỗi sang các định dạng khác, hãy thử sử dụng những thứ làm điều đó, như thế nào int()
.
eval
với input()
là một lỗ hổng bảo mật. Đừng đưa input()
vào một tuyên bố tệ hại và bạn sẽ ổn thôi.
eval
là một vấn đề bảo mật trong nhiều trường hợp.
input
thường lấy dữ liệu từ bảng điều khiển, người dùng có thể thoát khỏi chương trình và gõ bằng rm -R *
mọi cách ...
Rất nhiều câu trả lời hay ở đây, nhưng không có câu nào mô tả việc sử dụng eval()
trong ngữ cảnh của nó globals
và locals
kwargs, tức là eval(expression, globals=None, locals=None)
(xem tài liệu cho eval
ở đây ).
Chúng có thể được sử dụng để giới hạn các chức năng có sẵn thông qua eval
chức năng. Ví dụ: nếu bạn tải lên một trình thông dịch python mới locals()
, và globals()
sẽ giống nhau và trông giống như thế này:
>>>globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
'__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
'__package__': None, '__name__': '__main__'}
Chắc chắn có các chức năng trong builtins
mô-đun có thể gây thiệt hại đáng kể cho hệ thống. Nhưng có thể chặn mọi thứ và mọi thứ chúng tôi không muốn có sẵn. Hãy lấy một ví dụ. Giả sử chúng tôi muốn xây dựng một danh sách để thể hiện một miền của các lõi có sẵn trên một hệ thống. Đối với tôi, tôi có 8 nhân nên tôi muốn có một danh sách [1, 8]
.
>>>from os import cpu_count
>>>eval('[1, cpu_count()]')
[1, 8]
Tương tự như vậy tất cả đều __builtins__
có sẵn.
>>>eval('abs(-1)')
1
Đồng ý. Vì vậy, ở đó chúng ta thấy một chức năng mà chúng ta muốn tiếp xúc và một ví dụ về một phương thức (trong số nhiều phương pháp có thể phức tạp hơn nhiều) mà chúng ta không muốn tiếp xúc. Vì vậy, hãy chặn mọi thứ.
>>>eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable
Chúng tôi đã chặn tất cả các __builtins__
chức năng một cách hiệu quả và như vậy đã mang lại một mức độ bảo vệ cho hệ thống của chúng tôi. Tại thời điểm này, chúng ta có thể bắt đầu thêm lại các chức năng mà chúng ta muốn tiếp xúc.
>>>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
Bây giờ chúng tôi có cpu_count
chức năng có sẵn trong khi vẫn chặn mọi thứ chúng tôi không muốn. Theo tôi, đây là siêu mạnh mẽ và rõ ràng từ phạm vi của các câu trả lời khác, không phải là một triển khai chung. Có rất nhiều cách sử dụng cho một cái gì đó như thế này và miễn là nó được xử lý chính xác, cá nhân tôi cảm thấy eval
có thể được sử dụng một cách an toàn với giá trị lớn.
Lưu ý
Một điều thú vị khác về những điều này kwargs
là bạn có thể bắt đầu sử dụng tốc ký cho mã của mình. Giả sử bạn sử dụng eval như một phần của đường ống để thực thi một số văn bản đã nhập. Văn bản không cần phải có mã chính xác, nó có thể tuân theo một số định dạng tệp mẫu và vẫn thực thi bất cứ điều gì bạn muốn. Ví dụ:
>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
Trong Python 2.x input(...)
tương đương với eval(raw_input(...))
, trong Python 3.x raw_input
đã được đổi tên input
, điều mà tôi nghi ngờ dẫn đến sự nhầm lẫn của bạn (có lẽ bạn đang xem tài liệu về input
Python 2.x). Ngoài ra, eval(input(...))
sẽ hoạt động tốt trong Python 3.x, nhưng sẽ nâng cao TypeError
trong Python 2.
Trong trường hợp eval
này được sử dụng để ép buộc chuỗi được trả về từ input
một biểu thức và được giải thích. Nói chung đây được coi là thực hành xấu.
input
có nghĩa là những gì raw_input
đã làm trong 2.x.
Có thể một ví dụ sai lệch về việc đọc một dòng và giải thích nó.
Hãy thử eval(input())
và gõ "1+1"
- cái này sẽ in 2
. Eval đánh giá biểu thức.
eval()
đánh giá chuỗi đã truyền dưới dạng biểu thức Python và trả về kết quả. Ví dụ, eval("1 + 1")
diễn giải và thực hiện biểu thức"1 + 1"
và trả về kết quả (2).
Một lý do bạn có thể bị nhầm lẫn là bởi vì mã bạn trích dẫn liên quan đến một mức độ không xác định. Cuộc gọi chức năng bên trong (đầu vào) được thực hiện trước để người dùng thấy dấu nhắc "blah". Hãy tưởng tượng họ trả lời bằng "1 + 1" (trích dẫn được thêm vào cho rõ ràng, không nhập chúng khi chạy chương trình của bạn), hàm đầu vào trả về chuỗi đó, sau đó được chuyển đến hàm ngoài (eval) để giải thích chuỗi và trả về kết quả (2).
Tìm hiểu thêm về eval ở đây .
eval()
, như tên cho thấy, đánh giá các đối số được thông qua.
raw_input()
hiện đang input()
ở phiên bản python 3.x. Vì vậy, ví dụ phổ biến nhất cho việc sử dụng eval()
là sử dụng nó để cung cấp các chức nănginput()
được cung cấp trong phiên bản 2.x của python. raw_input trả về dữ liệu do người dùng nhập dưới dạng chuỗi, trong khi đầu vào đánh giá giá trị của dữ liệu được nhập và trả về.
eval(input("bla bla"))
do đó nhân rộng chức năng của input()
trong 2.x, tức là, đánh giá dữ liệu do người dùng nhập.
Tóm lại: eval()
đánh giá các đối số được truyền cho nó và do đó eval('1 + 1')
trả về 2.
Một trong những ứng dụng hữu ích eval()
là đánh giá biểu thức python từ chuỗi. Ví dụ tải từ biểu diễn chuỗi tệp của từ điển:
running_params = {"Greeting":"Hello "}
fout = open("params.dat",'w')
fout.write(repr(running_params))
fout.close()
Đọc nó dưới dạng một biến và chỉnh sửa nó:
fin = open("params.dat",'r')
diction=eval(fin.read())
diction["Greeting"]+="world"
fin.close()
print diction
Đầu ra:
{'Greeting': 'Hello world'}
eval
?
Tôi đến trễ để trả lời câu hỏi này, nhưng dường như không ai đưa ra câu trả lời rõ ràng cho câu hỏi.
Nếu người dùng nhập một giá trị số, input()
sẽ trả về một chuỗi.
>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'
Vì vậy, eval()
sẽ đánh giá giá trị trả về (hoặc biểu thức) là một chuỗi và trả về số nguyên / float.
>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>>
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14
Nguồn này là một thực hành xấu. int()
hoặc float()
nên được sử dụng thay vì eval()
trong trường hợp này.
>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Một tùy chọn khác nếu bạn muốn giới hạn chuỗi đánh giá theo nghĩa đen đơn giản là sử dụng ast.literal_eval()
. Vài ví dụ:
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}
Từ các tài liệu :
Đánh giá một cách an toàn một nút biểu thức hoặc một chuỗi chứa màn hình hiển thị bằng chữ hoặc container của Python. Chuỗi hoặc nút được cung cấp chỉ có thể bao gồm các cấu trúc bằng chữ Python sau: chuỗi, byte, số, bộ dữ liệu, danh sách, dicts, bộ, booleans và Không có.
Điều này có thể được sử dụng để đánh giá một cách an toàn các chuỗi chứa các giá trị Python từ các nguồn không đáng tin cậy mà không cần phải tự phân tích các giá trị. Nó không có khả năng đánh giá các biểu thức phức tạp tùy ý, ví dụ liên quan đến các toán tử hoặc lập chỉ mục.
Về lý do tại sao nó rất hạn chế, từ danh sách gửi thư :
Cho phép các biểu thức toán tử với nghĩa đen là có thể, nhưng phức tạp hơn nhiều so với triển khai hiện tại. Việc triển khai đơn giản không an toàn: bạn có thể sử dụng CPU và bộ nhớ không bị ràng buộc về cơ bản mà không cần nỗ lực (thử "9 ** 9 ** 9" hoặc "[Không] * 9 ** 9").
Về tính hữu dụng, hàm này rất hữu ích để "đọc lại" các giá trị bằng chữ và các thùng chứa được xâu chuỗi bởi repr (). Ví dụ, điều này có thể được sử dụng để tuần tự hóa theo định dạng tương tự nhưng mạnh hơn JSON.
ast.literal_eval
không hỗ trợ các nhà khai thác, trái với '1+1'
ví dụ của bạn . Tuy nhiên, nó không hỗ trợ danh sách, số, chuỗi, v.v. và vì vậy là một lựa chọn tốt cho các eval
trường hợp sử dụng thông thường .