Tôi đã xem xét đánh giá động về mã Python, và tìm thấy các hàm eval()và compile()hàm và execcâu lệnh.
Ai đó có thể vui lòng giải thích sự khác biệt giữa evalvà exec, và làm thế nào các chế độ khác nhau compile()phù hợp?
Tôi đã xem xét đánh giá động về mã Python, và tìm thấy các hàm eval()và compile()hàm và execcâu lệnh.
Ai đó có thể vui lòng giải thích sự khác biệt giữa evalvà exec, và làm thế nào các chế độ khác nhau compile()phù hợp?
Câu trả lời:
Về cơ bản, evalđược sử dụng để eval uate một tạo động biểu Python duy nhất, và execđược sử dụng để exec ute tạo động mã Python chỉ cho tác dụng phụ của nó.
evalvà execcó hai điểm khác biệt sau:
evalchỉ chấp nhận một biểu thức duy nhất , execcó thể mất một khối mã có báo cáo Python: vòng, try: except:, classvà chức năng / phương pháp definitions và vân vân.
Một biểu thức trong Python là bất cứ thứ gì bạn có thể có với giá trị trong một phép gán biến:
a_variable = (anything you can put within these parentheses is an expression)eval trả về giá trị của biểu thức đã cho, trong khi execbỏ qua giá trị trả về từ mã của nó và luôn trả về None(trong Python 2, nó là một câu lệnh và không thể được sử dụng như một biểu thức, vì vậy nó thực sự không trả về bất cứ thứ gì).
Trong các phiên bản 1.0 - 2.7, execlà một tuyên bố, bởi vì CPython cần tạo ra một loại đối tượng mã khác cho các hàm được sử dụng execcho các tác dụng phụ của nó bên trong hàm.
Trong Python 3, execlà một hàm; việc sử dụng nó không ảnh hưởng đến mã byte được biên dịch của hàm nơi nó được sử dụng.
Về cơ bản:
>>> a = 5
>>> eval('37 + a')   # it is an expression
42
>>> exec('37 + a')   # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47')   # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47')  # you cannot evaluate a statement
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    a = 47
      ^
SyntaxError: invalid syntaxCác compiletrong 'exec'chế độ biên dịch bất kỳ số lượng báo cáo thành bytecode mà ngầm luôn lợi nhuận None, trong khi ở 'eval'chế độ nó biên dịch một đơn biểu hiện thành bytecode rằng lợi nhuận giá trị của biểu thức đó.
>>> eval(compile('42', '<string>', 'exec'))  # code returns None
>>> eval(compile('42', '<string>', 'eval'))  # code returns 42
42
>>> exec(compile('42', '<string>', 'eval'))  # code returns 42,
>>>                                          # but ignored by execTrong 'eval'chế độ (và do đó với evalhàm nếu một chuỗi được truyền vào), compilesẽ xuất hiện một ngoại lệ nếu mã nguồn chứa các câu lệnh hoặc bất cứ thứ gì khác ngoài một biểu thức:
>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntaxTrên thực tế, câu lệnh "eval chỉ chấp nhận một biểu thức" chỉ áp dụng khi một chuỗi (chứa mã nguồn Python ) được truyền vào eval. Sau đó, nó được biên dịch nội bộ sang mã byte bằng cách sử dụng compile(source, '<string>', 'eval')Đây là nơi khác biệt thực sự đến từ.
Nếu một codeđối tượng (có chứa mã byte Python ) được truyền tới exechoặc eval, chúng sẽ hành xử giống hệt nhau , ngoại trừ thực tế là execbỏ qua giá trị trả về, vẫn Noneluôn trả về . Vì vậy, có thể sử dụng evalđể thực thi một cái gì đó có các câu lệnh, nếu bạn chỉ đưa compilenó vào mã byte trước đó thay vì chuyển nó dưới dạng một chuỗi:
>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>hoạt động mà không có vấn đề, mặc dù mã được biên dịch có chứa các câu lệnh. Nó vẫn trả về None, vì đó là giá trị trả về của đối tượng mã được trả về compile.
Trong 'eval'chế độ (và do đó với evalhàm nếu một chuỗi được truyền vào), compilesẽ xuất hiện một ngoại lệ nếu mã nguồn chứa các câu lệnh hoặc bất cứ thứ gì khác ngoài một biểu thức:
>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntaxexec và evalCác execchức năng (đó là một tuyên bố bằng Python 2 ) được sử dụng để thực hiện một tuyên bố động tạo hoặc chương trình:
>>> program = '''
for i in range(3):
    print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>> Các evalchức năng hoạt động tương tự đối với một biểu thức duy nhất , và trả về giá trị của biểu thức:
>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84execvà evalcả hai chấp nhận các chương trình / biểu thức để được chạy hoặc là một str, unicodehoặc bytesđối tượng có chứa mã nguồn, hoặc như là một codeđối tượng , trong đó có Python bytecode.
Nếu a str/ unicode/ byteschứa mã nguồn được truyền tới exec, nó hoạt động tương đương với:
exec(compile(source, '<string>', 'exec'))và evalhành vi tương tự như:
eval(compile(source, '<string>', 'eval'))Vì tất cả các biểu thức có thể được sử dụng làm câu lệnh trong Python (chúng được gọi là các Exprnút trong ngữ pháp trừu tượng của Python ; điều ngược lại là không đúng), bạn luôn có thể sử dụng execnếu bạn không cần giá trị trả về. Điều đó có nghĩa là, bạn có thể sử dụng một trong hai eval('my_func(42)')hoặc exec('my_func(42)'), sự khác biệt là evaltrả về giá trị được trả về my_funcvà execloại bỏ nó:
>>> def my_func(arg):
...     print("Called with %d" % arg)
...     return arg * 2
... 
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>> Trong số 2, chỉ execchấp nhận mã nguồn có chứa báo cáo, như def, for, while, import, hay class, báo cáo kết quả chuyển nhượng (aka a = 42), hoặc toàn bộ các chương trình:
>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntaxCả hai execvà evalchấp nhận 2 đối số vị trí bổ sung - globalsvà locals- đó là phạm vi biến toàn cục và biến cục bộ mà mã nhìn thấy. Những mặc định đến globals()và locals()trong phạm vi mà gọi là exechay eval, nhưng bất kỳ từ điển có thể được sử dụng cho globalsvà bất kỳ mappingcho locals(bao gồm dicttất nhiên). Chúng không chỉ được sử dụng để hạn chế / sửa đổi các biến mà mã nhìn thấy, mà còn thường được sử dụng để nắm bắt các biến mà execmã uted tạo ra:
>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}(Nếu bạn hiển thị giá trị của toàn bộ g, nó sẽ dài hơn nhiều, bởi vì execvà evalthêm mô-đun tích __builtins__hợp vào tự động nếu nó bị thiếu).
Trong Python 2, cú pháp chính thức cho execcâu lệnh thực sự exec code in globals, locals, như trong
>>> exec 'global a; a, b = 123, 42' in g, lTuy nhiên, cú pháp thay thế exec(code, globals, locals)luôn luôn được chấp nhận (xem bên dưới).
compileCác compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)built-in có thể được sử dụng để tăng tốc độ lời gọi lặp đi lặp lại của cùng mã với exechoặc evalbằng cách biên dịch các nguồn vào một codeđối tượng trước đó. Các modeđiều khiển thông số các loại đoạn mã compilechức năng chấp nhận và loại bytecode nó tạo ra. Các lựa chọn là 'eval', 'exec'và 'single':
'eval'chế độ mong đợi một biểu thức duy nhất và sẽ tạo ra mã byte mà khi chạy sẽ trả về giá trị của biểu thức đó :
>>> dis.dis(compile('a + b', '<string>', 'eval'))
  1           0 LOAD_NAME                0 (a)
              3 LOAD_NAME                1 (b)
              6 BINARY_ADD
              7 RETURN_VALUE'exec'chấp nhận bất kỳ loại cấu trúc python nào từ các biểu thức đơn lẻ đến toàn bộ các mô-đun mã và thực thi chúng như thể chúng là các câu lệnh cấp cao nhất của mô-đun. Đối tượng mã trả về None:
>>> dis.dis(compile('a + b', '<string>', 'exec'))
  1           0 LOAD_NAME                0 (a)
              3 LOAD_NAME                1 (b)
              6 BINARY_ADD
              7 POP_TOP                             <- discard result
              8 LOAD_CONST               0 (None)   <- load None on stack
             11 RETURN_VALUE                        <- return top of stack'single'là một hình thức hạn chế về 'exec'mà chấp nhận một mã nguồn có chứa một đơn tuyên bố (hoặc nhiều báo cáo phân cách bằng ;) nếu báo cáo kết quả cuối cùng là một tuyên bố ngôn luận, các bytecode kết quả cũng in các reprgiá trị mà biểu hiện cho đầu ra tiêu chuẩn (!) .
Một if- elif- elsedây chuyền, một vòng lặp với else, và tryvới nó except, elsevà finallykhối được coi là một tuyên bố duy nhất.
Một đoạn nguồn chứa 2 câu lệnh cấp cao nhất là một lỗi cho 'single', ngoại trừ trong Python 2 có một lỗi đôi khi cho phép nhiều câu lệnh toplevel trong mã; chỉ cái đầu tiên được biên soạn; phần còn lại bị bỏ qua:
Trong Python 2.7.8:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
>>> a
5Và trong Python 3.4.2:
>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    a = 5
        ^
SyntaxError: multiple statements found while compiling a single statementĐiều này rất hữu ích để tạo các vỏ Python tương tác. Tuy nhiên, giá trị của biểu thức không được trả về , ngay cả khi bạn evalmã kết quả.
Do đó, sự khác biệt lớn nhất execvà evalthực sự đến từ compilechức năng và các chế độ của nó.
Ngoài việc biên dịch mã nguồn thành mã byte, compilehỗ trợ biên dịch các cây cú pháp trừu tượng (các phân tích cú pháp của mã Python) thành codecác đối tượng; và mã nguồn thành các cây cú pháp trừu tượng ( ast.parseđược viết bằng Python và chỉ gọi compile(source, filename, mode, PyCF_ONLY_AST)); chúng được sử dụng ví dụ để sửa đổi mã nguồn một cách nhanh chóng và cũng để tạo mã động, vì thường dễ xử lý mã như một cây nút thay vì các dòng văn bản trong các trường hợp phức tạp.
Mặc dù evalchỉ cho phép bạn đánh giá một chuỗi chứa một biểu thức, bạn có thể evaltoàn bộ một câu lệnh hoặc thậm chí toàn bộ một mô-đun đã được compiled vào mã byte; đó là, với Python 2, printlà một câu lệnh và không thể được evaldẫn trực tiếp:
>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print("Python is cool")
      ^
SyntaxError: invalid syntaxcompilenó với 'exec'chế độ thành một codeđối tượng và bạn có thể làm eval  điều đó ; các evalchức năng sẽ trở lại None.
>>> code = compile('for i in range(3): print("Python is cool")',
                   'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is coolNếu một người nhìn vào evalvà execmã nguồn trong CPython 3, điều này rất rõ ràng; cả hai đều gọi PyEval_EvalCodevới cùng một đối số, sự khác biệt duy nhất là exectrả về rõ ràngNone .
execgiữa Python 2 và Python 3Một trong những khác biệt chính trong Python 2 là đó execlà một câu lệnh và evallà một hàm dựng sẵn (cả hai đều là các hàm dựng sẵn trong Python 3). Một thực tế nổi tiếng là cú pháp chính thức của execPython 2 là exec code [in globals[, locals]].
Không giống như phần lớn các hướng dẫn chuyển từ  2 đến 3 của Python dường như đề xuất , câu lệnh trong CPython 2 cũng có thể được sử dụng với cú pháp trông giống hệt như lời gọi hàm trong Python 3. Lý do là Python 0.9.9 đã được tích hợp sẵn trong chức năng! Và chức năng tích hợp đó đã được thay thế bằng câu lệnh ở đâu đó trước khi phát hành Python 1.0 .  exec execexec(code, globals, locals)exec
Vì mong muốn không phá vỡ khả năng tương thích ngược với Python 0.9.9, Guido van Rossum đã thêm một hack tương thích vào năm 1993 : nếu đó codelà một tuple có độ dài 2 hoặc 3 globalsvà localskhông được chuyển vào exectuyên bố theo cách khác, thì codenó sẽ được giải thích như thể phần tử thứ 2 và thứ 3 của tuple là globalsvà localstương ứng. Việc hack tương thích không được đề cập ngay cả trong tài liệu Python 1.4 (phiên bản sớm nhất có sẵn trực tuyến) ; và do đó không được biết đến bởi nhiều tác giả của các hướng dẫn và công cụ porting, cho đến khi nó được ghi lại vào tháng 11 năm 2012 :
Biểu thức đầu tiên cũng có thể là một tuple có độ dài 2 hoặc 3. Trong trường hợp này, các phần tùy chọn phải được bỏ qua. Hình thức
exec(expr, globals)tương đương vớiexec expr in globals, trong khi hình thứcexec(expr, globals, locals)tương đươngexec expr in globals, locals. Dạng tupleexeccung cấp khả năng tương thích với Python 3, trong đóexeclà một hàm chứ không phải là một câu lệnh.
Có, trong CPython 2.7, nó được gọi một cách cẩn thận là một tùy chọn tương thích về phía trước (tại sao mọi người lại nhầm lẫn rằng có một tùy chọn tương thích ngược), khi nó thực sự đã ở đó để tương thích ngược trong hai thập kỷ .
Do đó, trong khi đó execlà một câu lệnh trong Python 1 và Python 2 và một hàm dựng sẵn trong Python 3 và Python 0.9.9,
>>> exec("print(a)", globals(), {'a': 42})
42đã có hành vi giống hệt nhau trong mọi phiên bản Python được phát hành rộng rãi từ trước đến nay; và hoạt động trong Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) và IronPython 2.6.1 (kudos đối với họ theo sát hành vi không có giấy tờ của CPython).
Điều bạn không thể làm trong Pythons 1.0 - 2.7 với hack tương thích của nó, là lưu trữ giá trị trả về của execmột biến:
Python 2.7.11+ (default, Apr 17 2016, 14:00:29) 
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
  File "<stdin>", line 1
    a = exec('print(42)')
           ^
SyntaxError: invalid syntax(điều này sẽ không hữu ích trong Python 3, như execluôn luôn trả về None) hoặc chuyển tham chiếu đến exec:
>>> call_later(exec, 'print(42)', delay=1000)
  File "<stdin>", line 1
    call_later(exec, 'print(42)', delay=1000)
                  ^
SyntaxError: invalid syntaxMà một mô hình mà ai đó thực sự có thể đã sử dụng, mặc dù không thể;
Hoặc sử dụng nó trong một danh sách hiểu:
>>> [exec(i) for i in ['print(42)', 'print(foo)']
  File "<stdin>", line 1
    [exec(i) for i in ['print(42)', 'print(foo)']
        ^
SyntaxError: invalid syntaxđó là lạm dụng việc hiểu danh sách (sử dụng forvòng lặp thay thế!).
42cũng là một biểu thức và bạn không thể sử dụng nó @như một trang trí.
                    decorator ::=  "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE; tức là bạn không thể sử dụng các biểu thức tùy ý làm trang trí, CHỈ một mã định danh (có thể được chấm), theo sau là các đối số cuộc gọi tùy chọn.
                    a = b = clà một tuyên bố hoàn toàn hợp lệ, cũng như phía bên tay phải của nó b = c- không phải là một biểu thức.
                    execkhông phải là biểu thức: một câu lệnh trong Python 2.x và một hàm trong Python 3.x. Nó biên dịch và ngay lập tức đánh giá một câu lệnh hoặc tập hợp câu lệnh chứa trong một chuỗi. Thí dụ:
exec('print(5)')           # prints 5.
# exec 'print 5'     if you use Python 2.x, nor the exec neither the print is a function there
exec('print(5)\nprint(6)')  # prints 5{newline}6.
exec('if True: print(6)')  # prints 6.
exec('5')                 # does nothing and returns nothing.evallà một hàm dựng sẵn ( không phải là một câu lệnh), để đánh giá một biểu thức và trả về giá trị mà biểu thức tạo ra. Thí dụ:
x = eval('5')              # x <- 5
x = eval('%d + 6' % x)     # x <- 11
x = eval('abs(%d)' % -100) # x <- 100
x = eval('x = 5')          # INVALID; assignment is not an expression.
x = eval('if 1: x = 4')    # INVALID; if is a statement, not an expression.compilelà một phiên bản cấp thấp hơn của execvà eval. Nó không thực thi hoặc đánh giá các câu lệnh hoặc biểu thức của bạn, nhưng trả về một đối tượng mã có thể làm điều đó. Các chế độ như sau:
compile(string, '', 'eval')trả về đối tượng mã sẽ được thực thi nếu bạn đã thực hiện eval(string). Lưu ý rằng bạn không thể sử dụng các câu lệnh trong chế độ này; chỉ một biểu thức (đơn) là hợp lệ.compile(string, '', 'exec')trả về đối tượng mã sẽ được thực thi nếu bạn đã thực hiện exec(string). Bạn có thể sử dụng bất kỳ số lượng báo cáo ở đây.compile(string, '', 'single')giống như execchế độ, nhưng nó sẽ bỏ qua mọi thứ trừ câu lệnh đầu tiên. Lưu ý rằng một câu lệnh if/ elsevới kết quả của nó được coi là một câu lệnh.exec()thực tế bây giờ là một hàm.
                    execlà một tuyên bố trong phiên bản bạn đang nhắm mục tiêu, nên việc bao gồm các parens đó là lừa đảo và nếu bạn cố gắng sử dụng in globals, locals, cũng có lỗi.
                    exec hỗ trợ dấu ngoặc đơn và chức năng như lời gọi trong Python 2 .
                    x = (y), điều đó có thể đúng. Một chức năng chuyển đổi câu lệnh khác là print; so sánh kết quả của print(1, 2, 3)python 2 và 3.
                    exec là cho tuyên bố và không trả lại bất cứ điều gì. eval là cho biểu thức và trả về giá trị của biểu thức.
biểu thức có nghĩa là "một cái gì đó" trong khi tuyên bố có nghĩa là "làm một cái gì đó".
[i for i in globals().values() if hasattr(i, '__call__')][0]một tuyên bố hoặc biểu hiện? Nếu đó là một biểu thức, tại sao tôi không thể sử dụng nó với@tư cách là người trang trí?