Python's eval () làm gì?


306

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?


4
Hàm Eval cố gắng thực thi và giải thích chuỗi (đối số) được truyền cho nó dưới dạng mã python. x = 1 print (eval ('x + 1')) Đầu ra của đoạn mã trên sẽ là 2. Nhược điểm của cách tiếp cận đó là, người dùng có được sự độc lập của mã viết có thể dẫn đến tình trạng tàn phá. Bạn có thể hạn chế người dùng khỏi truy cập nhiều biến và phương thức bằng cách chuyển tham số toàn cục và cục bộ trong hàm eval.
ATIF IBAD KHAN

Câu trả lời:


276

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

25
haha, đó là một ví dụ tầm thường, nhưng bạn có thể cho phép người dùng gõ một lệnh tùy ý và để python thực thi nó. Vì vậy, bạn có thể có người dùng gõ vào một chuỗi lệnh và sau đó python chạy nó dưới dạng mã. Vì vậy, ví dụ: eval ("__ import __ ('os'). Remove ('file')").
BYS2

60
Nó sẽ có vẻ vô dụng cho đến khi bạn tìm thấy một nhu cầu cho nó. Nó được sử dụng trên các trang web như codepad.org để cho phép bạn thực thi các tập lệnh trong môi trường thử nghiệm. 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ó.
George Cummins

6
@GeorgeCummins, codepad.org không sử dụng eval, cũng không thể làm những gì nó làm với eval.
Mike Graham

16
@GeorgeCummins: codepag.org chạy mọi thứ trong hộp cát: một nhà tù chroot với kiểm tra ptrace trong một máy ảo để ngăn chặn mã độc làm bất cứ điều gì xấu. Phức tạp hơn nhiều so với một eval đơn giản. Ngoài ra, eval là đặc trưng của Python. codepad hỗ trợ một loạt các ngôn ngữ.
FogleBird

4
@GeorgeCummins, codepad chạy một hệ thống rất phức tạp để chạy các chương trình tùy ý một cách an toàn. 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.
Mike Graham

165

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())osnhậ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().


14
Bạn có nghĩa là sử dụng evalvớ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.
Rohmer

19
@Rohmer, dữ liệu không an toàn có thể đến từ mọi nơi: yêu cầu web, trường nhập mẫu, đọc tệp, ... không chỉ từ đầu vào giao diện điều khiển. Ngay cả khi bạn tự viết các tệp, nó vẫn có thể chứa đầu vào ban đầu đến từ một nguồn không đáng tin cậy. Vì vậy, evallà một vấn đề bảo mật trong nhiều trường hợp.
sanderd17

3
inputthườ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 ...
cz

63

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ó globalslocalskwargs, 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 evalchứ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 builtinsmô-đ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_countchứ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 evalcó 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 kwargslà 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]

29

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ề inputPython 2.x). Ngoài ra, eval(input(...))sẽ hoạt động tốt trong Python 3.x, nhưng sẽ nâng cao TypeErrortrong Python 2.

Trong trường hợp evalnày được sử dụng để ép buộc chuỗi được trả về từ inputmột biểu thức và được giải thích. Nói chung đây được coi là thực hành xấu.


Hoặc đó là một cuốn sách Python 3.x, inputcó nghĩa là những gì raw_inputđã làm trong 2.x.
dan04

1
Vâng, điều đó xảy ra với tôi sau khi tôi viết câu trả lời ban đầu của mình, và đó rõ ràng là trường hợp.
zeekay

6

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.


Tại sao tôi nên gõ nó giữa các trích dẫn? Đầu vào đang nhận một chuỗi và chuyển nó đến eval, không thực thi mã, vì vậy tôi sẽ ổn nếu tôi chỉ cần gõ 1 + 1 ...?
JC Rocnhoe

Có điều là bạn đang trộn P2.x và 3.x. Trong Python 2, mã của bạn hoạt động, nhưng không có nghĩa gì để đánh giá hai lần. Trong python 3 thì không, và trả về một chuỗi.
JC Rocnhoe

6

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 .


6

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.


6

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

7
Làm thế nào để trả lời câu hỏi này hỏi cái gì eval?
jkd 28/03/2015

4

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

3

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.


1
ast.literal_evalkhô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 evaltrường hợp sử dụng thông thường .
benjimin

@benjimin oh bạn nói đúng - đó chỉ là một sự châm biếm rằng nó chấp nhận 1 + 1! stackoverflow.com/questions / 40584417 / từ
Brian Burns
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.