Làm cách nào để thực thi một chuỗi chứa mã Python trong Python?
Làm cách nào để thực thi một chuỗi chứa mã Python trong Python?
Câu trả lời:
Đối với câu lệnh, sử dụng exec(string)
(Python 2/3) hoặc exec string
(Python 2):
>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world
Khi bạn cần giá trị của biểu thức, hãy sử dụng eval(string)
:
>>> x = eval("2+2")
>>> x
4
Tuy nhiên, bước đầu tiên nên tự hỏi mình nếu bạn thực sự cần. Mã thực thi thường là vị trí cuối cùng: Thật chậm, xấu và nguy hiểm nếu nó có thể chứa mã do người dùng nhập. Bạn nên luôn luôn xem xét các lựa chọn thay thế trước tiên, chẳng hạn như các hàm bậc cao hơn, để xem liệu những thứ này có thể đáp ứng tốt hơn nhu cầu của bạn không.
if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42
vv, sau đó họ có thể viết là exec ("x.%s = 42" % s)
. Đối với trường hợp phổ biến này (trong đó bạn chỉ cần truy cập thuộc tính của đối tượng được lưu trữ trong một chuỗi), có một chức năng nhanh hơn, sạch hơn và an toàn hơn getattr
: chỉ cần viết getattr(x, s) = 42
để có nghĩa tương tự.
setattr(x, s, 42)
sao? Tôi đã thử getattr(x, 2) = 42
và thất bại vớican't assign to function call: <string>, line 1
setattr(x, s, 42)
là cú pháp đúng. Ngạc nhiên là phải mất rất lâu để lỗi đó được bắt. Dù sao đi nữa, vấn đề là ở chỗ getattr
và setattr
là một giải pháp thay thế exec
khi tất cả những gì bạn muốn là có được một thành viên độc đoán, tìm kiếm theo chuỗi.
Trong ví dụ này, một chuỗi được thực thi dưới dạng mã bằng hàm 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()
exec
(trừ khi bạn biết chuỗi mã đến từ một nguồn đáng tin cậy).
eval
và exec
là giải pháp chính xác, và chúng có thể được sử dụng một cách an toàn hơn .
Như đã thảo luận trong hướng dẫn tham khảo của Python và được giải thích rõ ràng trong hướng dẫn này , các hàm eval
và exec
hàm có hai tham số bổ sung cho phép người dùng chỉ định các biến và hàm toàn cục và cục bộ nào khả dụng.
Ví dụ:
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
Về bản chất, bạn đang xác định không gian tên trong đó mã sẽ được thực thi.
eval
hoặc exec
dự định sẽ được sử dụng như exec(input("Type what you want"))
? Có nhiều trường hợp một chương trình có thể viết một thủ tục hoặc một hàm là kết quả của một tính toán; các chức năng kết quả sẽ an toàn và nhanh chóng (một khi được đánh giá) như bất kỳ phần nào khác của một chương trình tốt và được viết tốt. Một chương trình không an toàn có chứa exec
không nguy hiểm hơn một chương trình không an toàn gây thiệt hại vì nó exec
không mang lại bất kỳ đặc quyền mới nào cho chương trình.
exec
vàeval
exec
và eval
trong Python là rất khó chịu.Từ câu trả lời hàng đầu (nhấn mạnh của tôi):
Đối với báo cáo, sử dụng
exec
.Khi bạn cần giá trị của một biểu thức, sử dụng
eval
.Tuy nhiên, bước đầu tiên nên tự hỏi mình nếu bạn thực sự cần. Mã thực thi thường là vị trí cuối cùng : Thật chậm, xấu và nguy hiểm nếu nó có thể chứa mã do người dùng nhập. Bạn nên luôn luôn xem xét các lựa chọn thay thế trước tiên, chẳng hạn như các hàm bậc cao hơn , để xem liệu những thứ này có thể đáp ứng tốt hơn nhu cầu của bạn không.
Từ lựa chọn thay thế để thực hiện / eval?
đặt và nhận giá trị của các biến với các tên trong chuỗi
[while
eval
] sẽ hoạt động, thông thường không nên sử dụng các tên biến mang ý nghĩa đối với chính chương trình.Thay vào đó, tốt hơn nên sử dụng một dict.
Từ http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (nhấn mạnh của tôi)
Python không phải là PHP
Đừng cố gắng phá vỡ các thành ngữ Python vì một số ngôn ngữ khác làm điều đó khác đi. Không gian tên là trong Python vì một lý do và chỉ vì nó cung cấp cho bạn công cụ,
exec
điều đó không có nghĩa là bạn nên sử dụng công cụ đó.
Từ http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (nhấn mạnh của tôi)
Vì vậy, eval không an toàn, ngay cả khi bạn loại bỏ tất cả các phần chung và nội dung!
Vấn đề với tất cả những nỗ lực này để bảo vệ eval () là chúng là danh sách đen . Họ rõ ràng loại bỏ những thứ có thể nguy hiểm. Đó là một trận thua vì nếu chỉ còn một mục trong danh sách, bạn có thể tấn công hệ thống .
Vì vậy, eval có thể được thực hiện an toàn? Khó nói. Tại thời điểm này, dự đoán tốt nhất của tôi là bạn không thể gây hại nếu bạn không thể sử dụng bất kỳ dấu gạch dưới kép nào, vì vậy có thể nếu bạn loại trừ bất kỳ chuỗi nào có dấu gạch dưới kép bạn vẫn an toàn. Có lẽ...
Từ http://stoolpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (nhấn mạnh của tôi):
Đầu tiên,
exec
làm cho con người khó đọc mã của bạn hơn . Để tìm hiểu điều gì đang xảy ra, tôi không cần phải đọc mã của bạn, tôi phải đọc mã của bạn, tìm ra chuỗi nào sẽ tạo ra, sau đó đọc mã ảo đó. Vì vậy, nếu bạn đang làm việc trong một nhóm hoặc xuất bản phần mềm nguồn mở hoặc yêu cầu trợ giúp ở đâu đó như StackOverflow, bạn sẽ khiến những người khác khó khăn hơn trong việc giúp đỡ bạn. Và nếu có bất kỳ cơ hội nào mà bạn sẽ gỡ lỗi hoặc mở rộng mã này 6 tháng kể từ bây giờ, bạn sẽ tự làm khó mình hơn.
cfg.yaml
): reldir : ../my/dir/
và reldir = cfg[reldir]
. Tuy nhiên, vì mã python này sẽ chạy trên cả Windows và Linux, tôi cần điều này để điều chỉnh các bộ chia đường dẫn hệ điều hành khác nhau; hoặc \\
hoặc /
. Vì vậy, tôi sử dụng reldir : os.path.join('..','my','dir')
trong tập tin cấu hình. Nhưng điều này chỉ dẫn đến reldir
chuỗi ký tự này, không được đánh giá, vì vậy tôi không thể mở tệp reldir
. Bạn có một đề nghị?
Bạn hoàn thành việc thực thi mã bằng cách sử dụng exec, như với phiên IDLE sau:
>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']
4
Như những người khác đã đề cập, đó là "exec" ..
nhưng, trong trường hợp mã của bạn chứa các biến, bạn có thể sử dụng "toàn cầu" để truy cập nó, cũng để ngăn trình biên dịch đưa ra lỗi sau:
NameError: tên 'p_variable' không được xác định
exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)
Điều đáng nói, đó exec
là anh trai của nó tồn tại cũng được gọi làexecfile
nếu bạn muốn gọi một tệp python. Điều đó đôi khi tốt nếu bạn đang làm việc trong gói bên thứ ba có IDE khủng khiếp và bạn muốn mã bên ngoài gói của họ.
Thí dụ:
execfile('/path/to/source.py)'
hoặc là:
exec(open("/path/to/source.py").read())
Tôi đã thử khá nhiều thứ, nhưng điều duy nhất làm việc là như sau:
temp_dict = {}
exec("temp_dict['val'] = 10")
print(temp_dict['val'])
đầu ra:
10
Ok .. Tôi biết đây không phải là một câu trả lời chính xác, nhưng có lẽ là một lưu ý cho những người nhìn vào điều này như tôi. Tôi muốn thực thi mã cụ thể cho những người dùng / khách hàng khác nhau nhưng cũng muốn tránh exec / eval. Ban đầu tôi tìm cách lưu trữ mã trong cơ sở dữ liệu cho mỗi người dùng và thực hiện các thao tác trên.
Tôi đã kết thúc việc tạo các tệp trên hệ thống tệp trong thư mục 'customer_filters' và sử dụng mô-đun 'imp', nếu không có bộ lọc nào được áp dụng cho khách hàng đó, thì nó chỉ được thực hiện
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)
vì vậy customerName = "jj" sẽ thực thi application_address_filter từ tệp customer_filters \ jj_filter.py