Làm cách nào để thực thi một chuỗi chứa mã Python trong Python?


357

Làm cách nào để thực thi một chuỗi chứa mã Python trong Python?


5
Công bằng mà nói, không giống như câu hỏi khác, câu hỏi này không phải là một bản sao. Và những người khác đã đăng câu hỏi cơ bản hơn nhiều so với điều này.
DNS

8
Câu trả lời đúng, tất nhiên, hầu như luôn luôn là không nên!
bobince

23
@S. Lott: Gần đây không đọc FAQ? "Cũng rất tốt để hỏi và trả lời câu hỏi lập trình của riêng bạn, nhưng giả vờ bạn đang gặp Jeopardy: diễn đạt nó dưới dạng câu hỏi". Đây không phải là một câu hỏi tồi. +1
Devin Jeanpierre

49
@ S.Lott: Bạn không. Bạn không cần. Nếu câu hỏi chưa có trên trang web, thì đó là trò chơi công bằng (theo Câu hỏi thường gặp, như đã chỉ ra). Chỉ cần trả lời mọi câu hỏi như thể OP cần giúp đỡ. Họ có thể không, nhưng anh chàng tiếp theo đọc câu hỏi của họ có lẽ sẽ làm được. Chỉ cần 2 xu của tôi.
Bill the Lizard

1
Đối với tất cả các bạn nói không làm điều đó: Tôi có một trường hợp mà tôi thực sự cần phải làm. Nó liên quan đến xử lý phân tán.
sudo

Câu trả lời:


331

Đố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.


1
nhưng phạm vi của mã được thực thi bởi 'exec' thì sao? nó có được lồng không
jondinham

21
Một trường hợp phổ biến khi ai đó muốn sử dụng 'exec' là một cái gì đó giống như if s=='foo': x.foo = 42 elif s=='bar': x.bar = 42vv, 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ự.
ShreevatsaR

5
Làm thế nào là exec bất kỳ chậm hơn so với trình thông dịch python?
Cris Stringfellow

6
@ShreevatsaR không phải ý bạn setattr(x, s, 42)sao? Tôi đã thử getattr(x, 2) = 42và thất bại vớican't assign to function call: <string>, line 1
Tanner Semerad

6
@Tanner: Hừm. Có thực sự 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ỗ getattrsetattrlà một giải pháp thay thế execkhi 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.
ShreevatsaR

68

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

1
hoán đổi stdout và stderr như thế làm tôi rất lo lắng. có vẻ như nó có thể gây ra vấn đề an ninh lớn. Có cách nào khác không?
Narcolapser

1
@Narcolapser bạn nên quan tâm nhiều hơn đến việc sử dụng exec(trừ khi bạn biết chuỗi mã đến từ một nguồn đáng tin cậy).
bruno Desthuilliers

27

evalexeclà 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 evalexechà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.


9
Không thể làm cho eval an toàn: Eval thực sự nguy hiểm . Nếu bạn lấy mã từ tôi và đánh giá nó, tôi có thể tách chương trình Python của bạn. Trò chơi kết thúc.
Ned Batchelder

3
@ v.oddou Tôi đã phản hồi lại tuyên bố của alan, "eval và exec .. có thể được sử dụng một cách an toàn." Điều này là sai. Nếu ai đó nói, "bash có thể được sử dụng một cách an toàn", đó cũng là sai. Bash là nguy hiểm. Đó là một mối nguy hiểm cần thiết, tuy nhiên nguy hiểm. Cho rằng eval có thể được thực hiện an toàn chỉ đơn giản là sai.
Ned Batchelder

1
@NedBatchelder đúng vậy. và liên kết bạn trỏ đến là tài liệu tốt. với quyền lực đi kèm với trách nhiệm, vì vậy vấn đề chỉ đơn giản là nhận thức được sức mạnh tiềm tàng của eval. và nếu chúng ta quyết định rằng sức mạnh = nguy hiểm.
v.oddou

3
@NedBatchelder Nhiều đoạn mã được viết bằng Python cũng có thể nguy hiểm, nhưng tại sao bạn lại cho rằng evalhoặc execdự đị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 execkhô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ó execkhông mang lại bất kỳ đặc quyền mới nào cho chương trình.
Thomas Baruchel

1
@ThomasBaruchel một lần nữa, quan điểm của tôi là chống lại quan niệm rằng eval hoặc exec có thể được làm cho an toàn. Đặc biệt, câu trả lời này tuyên bố rằng việc kiểm soát toàn cầu và người dân địa phương sẽ giúp sử dụng chúng một cách an toàn. Điều đó là sai. Bất cứ khi nào bạn sử dụng exec và eval, bạn phải biết chính xác mã nào đang được thực thi. Nếu bạn không, thì bạn đang mở cho các hoạt động nguy hiểm.
Ned Batchelder

21

Hãy nhớ rằng từ phiên bản 3 execlà một chức năng!
vì vậy luôn luôn sử dụng exec(mystring)thay vì exec mystring.


11

eval()chỉ dành cho biểu thức, trong khi eval('x+1')hoạt động, eval('x=1')sẽ không hoạt động chẳng hạn. Trong trường hợp đó, tốt hơn là sử dụng exechoặc thậm chí tốt hơn: cố gắng tìm giải pháp tốt hơn :)


11

Tránh execeval

Sử dụng execevaltrong Python là rất khó chịu.

Có những lựa chọn thay thế tốt hơn

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.

Nó không phải là thành ngữ

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ụ đó.

Nó nguy hiểm

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

Thật khó đọc và hiểu

Từ http://stoolpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (nhấn mạnh của tôi):

Đầu tiên, execlà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.


"Dự đoán tốt nhất của tôi là bạn không thể làm hại gì nếu bạn không thể sử dụng bất kỳ dấu gạch dưới kép nào" - Bạn có thể tạo một chuỗi chứa hai dấu gạch dưới, và sau đó gọi eval chuỗi đó.
Stanley Bak

lời khuyên tốt, trừ khi bạn đang viết một trình tạo mã, hoặc người chạy công việc hoặc tương tự ... mà hầu hết mọi người ở đây có thể đang làm.
Erik Aronesty

Tôi phải nhập một đường dẫn tương đối từ tệp cấu hình ( cfg.yaml): reldir : ../my/dir/ 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 reldirchuỗ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ị?
Marie. P.

9

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

1
Điều này không hoạt động trong python bình thường. Ít nhất là không ở trăn 3.
Thomas Ahle

6

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)


4

Điều đáng nói, đó execlà 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())



1

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


0

Giải pháp hợp lý nhất sẽ là sử dụng hàm eval () tích hợp. Giải pháp khác là ghi chuỗi đó vào tệp python tạm thời và thực thi nó.


0

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


1
Làm thế nào bạn quản lý an ninh? Làm thế nào để bạn biết khách hàng sẽ không lạm dụng đặc quyền này?
JSBach
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.