2010-05-25 23 views
32

Tôi đang cố gắng chạy một đoạn mã python bằng cách sử dụng exec.globals và locals trong python exec()

my_code = """ 
class A(object): 
    pass 

print 'locals: %s' % locals() 
print 'A: %s' % A 

class B(object): 
    a_ref = A 
""" 

global_env = {} 
local_env = {} 
my_code_AST = compile(my_code, "My Code", "exec") 
exec(my_code_AST, global_env, local_env) 

print local_env 

mà kết quả trong đầu ra sau đây

locals: {'A': <class 'A'>} 
A: <class 'A'> 
Traceback (most recent call last): 
    File "python_test.py", line 16, in <module> 
    exec(my_code_AST, global_env, local_env) 
    File "My Code", line 8, in <module> 
    File "My Code", line 9, in B 
NameError: name 'A' is not defined 

Tuy nhiên, nếu tôi thay đổi mã này -

my_code = """ 
class A(object): 
    pass 

print 'locals: %s' % locals() 
print 'A: %s' % A 

class B(A): 
    pass 
""" 

global_env = {} 
local_env = {} 
my_code_AST = compile(my_code, "My Code", "exec") 
exec(my_code_AST, global_env, local_env) 

print local_env 

sau đó nó hoạt động tốt - đưa ra các kết quả như sau -

locals: {'A': <class 'A'>} 
A: <class 'A'> 
{'A': <class 'A'>, 'B': <class 'B'>} 

Rõ ràng A được đặt trước ent và có thể truy cập - những gì đang xảy ra trong đoạn mã đầu tiên? Tôi đang sử dụng 2.6.5, cổ vũ,

Colin

* CẬP NHẬT 1 *

Nếu tôi kiểm tra người dân địa phương() bên trong lớp -

my_code = """ 
class A(object): 
    pass 

print 'locals: %s' % locals() 
print 'A: %s' % A 

class B(object): 
    print locals() 
    a_ref = A 
""" 

global_env = {} 
local_env = {} 
my_code_AST = compile(my_code, "My Code", "exec") 
exec(my_code_AST, global_env, local_env) 

print local_env 

Sau đó, nó trở thành rõ ràng rằng người dân địa phương() không giống nhau ở cả hai địa điểm -

locals: {'A': <class 'A'>} 
A: <class 'A'> 
{'__module__': '__builtin__'} 
Traceback (most recent call last): 
    File "python_test.py", line 16, in <module> 
    exec(my_code_AST, global_env, local_env) 
    File "My Code", line 8, in <module> 
    File "My Code", line 10, in B 
NameError: name 'A' is not defined 

Tuy nhiên, nếu tôi làm điều này, không có vấn đề -

def f(): 
    class A(object): 
    pass 

    class B(object): 
    a_ref = A 

f() 

print 'Finished OK' 

* CẬP NHẬT 2 *

ok, vì vậy các tài liệu ở đây - http://docs.python.org/reference/executionmodel.html

'Một định nghĩa lớp là một thực thi tuyên bố có thể sử dụng và xác định tên. Các tham chiếu này tuân theo các quy tắc thông thường để phân giải tên. Không gian tên của định nghĩa lớp trở thành từ điển thuộc tính của lớp. Các tên được định nghĩa ở phạm vi lớp không hiển thị trong các phương thức. ' Có vẻ như với tôi rằng 'A' nên có sẵn như là một biến miễn phí trong câu lệnh thực thi là định nghĩa của B, và điều này xảy ra khi chúng ta gọi f() ở trên, nhưng không phải khi chúng ta sử dụng exec (). Điều này có thể dễ dàng hơn hiển thị với những điều sau đây -

my_code = """ 
class A(object): 
    pass 

print 'locals in body: %s' % locals() 
print 'A: %s' % A 

def f(): 
    print 'A in f: %s' % A 

f() 

class B(object): 
    a_ref = A 
""" 

mà kết quả đầu ra

locals in body: {'A': <class 'A'>} 
A: <class 'A'> 
Traceback (most recent call last): 
    File "python_test.py", line 20, in <module> 
    exec(my_code_AST, global_env, local_env) 
    File "My Code", line 11, in <module> 
    File "My Code", line 9, in f 
NameError: global name 'A' is not defined 

Vì vậy, tôi đoán câu hỏi mới là - tại sao không phải là những người dân địa phương được tiếp xúc như biến miễn phí trong các chức năng và định nghĩa lớp - nó có vẻ giống như một kịch bản đóng cửa khá chuẩn.

+0

Tốt. Không bao giờ để ý điều đó. – zefciu

+0

Có vẻ như vấn đề tương tự như trong câu hỏi này: http://stackoverflow.com/questions/2749655/why-are-closures-broken-within-exec – interjay

+0

cảm ơn con trỏ - Tôi không phải là một guru trăn, nhưng có vẻ như khi tôi in các locals(), A * đã * được biên dịch thành một biến cục bộ - nghĩa là nó không biết phải làm gì với nó. Câu trả lời trong câu hỏi bạn đánh dấu là - 'Không có cách nào để biên dịch biết rằng đó là freevar, do đó nó biên dịch nó thành tham chiếu toàn cầu' Vấn đề ở đây có vẻ là người dân địa phương() được định nghĩa lại bên trong cơ thể của B khi sử dụng exec, nhưng không phải khi sử dụng một chức năng (xem cập nhật trong câu hỏi)? Có thể dễ dàng là sự hiểu lầm của tôi về hàm ý của câu trả lời đó ... – hawkett

Trả lời

17

Vâng, tôi tin rằng đó là lỗi triển khai hoặc quyết định thiết kế không có giấy tờ. Điểm mấu chốt của vấn đề là một hoạt động liên kết tên trong phạm vi mô-đun phải liên kết với một biến toàn cầu. Cách mà nó đạt được là khi ở cấp mô-đun, globals() IS locals() (thử một cái trong trình thông dịch), vì vậy khi bạn thực hiện bất kỳ ràng buộc tên nào, nó gán nó như bình thường cho người dân địa phương () từ điển, cũng là các hình cầu, do đó một biến toàn cầu được tạo ra.Khi bạn tìm kiếm một biến, trước tiên bạn kiểm tra người dân địa phương hiện tại của mình và nếu không tìm thấy tên, bạn kiểm tra đệ quy các vị trí chứa phạm vi cho tên biến cho đến khi bạn tìm biến hoặc đạt đến phạm vi mô-đun. Nếu bạn đạt được điều đó, bạn kiểm tra các hình cầu, được cho là địa phương của phạm vi mô-đun.

>>> exec(compile("import sys\nprint sys._getframe().f_code.co_name", "blah", "exec"), {}, {}) 
<module> 
>>> exec("a = 1\nclass A(object):\n\tprint a\n", {}, {}) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<string>", line 2, in <module> 
    File "<string>", line 3, in A 
NameError: name 'a' is not defined 
>>> d = {} 
>>> exec("a = 1\nclass A(object):\n\tprint a\n", d,d) 
1 

Hành vi này là lý do tại sao thừa kế làm việc (người dân địa phương phạm vi Tên-tra cứu sử dụng mã đối tượng của(), mà thực sự đã có A trong nó).

Cuối cùng, đó là một hack xấu xí trong việc thực hiện CPython, đặc biệt vỏ bọc globals tra cứu. Nó cũng gây ra một số trường hợp nhân tạo vô nghĩa - ví dụ .:

>>> def f(): 
...  global a 
...  a = 1 
... 
>>> f() 
>>> 'a' in locals() 
True 

Xin lưu ý rằng đây là tất cả các suy luận của tôi dựa trên rối tung với người phiên dịch trong khi đọc mục 4.1 (Naming và ràng buộc) của tham chiếu ngôn ngữ python. Trong khi điều này không phải là dứt khoát (tôi đã không mở các nguồn của CPython), tôi khá chắc chắn tôi là chính xác về hành vi.

+0

Ok - cảm ơn thông tin đó - đó là một rắc rối :) lý do chính tôi muốn sử dụng người dân địa phương như vậy, là để có được tất cả những thứ được xác định trong chuỗi mã, mà không có tất cả các thứ khác mà python đặt trong globals. Nếu tôi in các globals() bên trong mã đó, một từ điển lớn của nó, có ý nghĩa, nhưng bây giờ tôi không biết làm thế nào để có được những thứ đã được định nghĩa trong chuỗi mã vào một từ điển - tức là từ điển chỉ có {'A', , 'B', } trong đó. Tôi không muốn phải tự xóa tất cả mọi thứ ra, và tôi không biết trước những gì trong chuỗi mã. – hawkett

+1

Tôi sẽ đánh dấu câu hỏi này được trả lời - Tôi đã gửi một lỗi trăn tại đây - http://bugs.python.org/issue8819 - chúc mừng. – hawkett

+1

Lỗi tôi đã gửi là bản sao. Tôi đã thực hiện một đối số cho việc này để được sửa trong 2.6+ tại đây - http://bugs.python.org/issue991196 – hawkett

2

Nếu câu hỏi của bạn là cách nhận câu lệnh exec để xử lý như phạm vi tệp, tôi đã làm theo một số gợi ý trong câu hỏi và lỗi được liên kết và làm việc đó bằng cách chuyển một từ điển duy nhất cho các địa cầu và địa phương. Rõ ràng phạm vi tệp là một trường hợp đặc biệt trong đó các khai báo cục bộ được đặt tự động trong phạm vi toàn cục.

exec code in dict() 
6

Sau print locals()globals(), bạn sẽ tìm thấy lý do tại sao exec ném "không được định nghĩa" ngoại lệ, và bạn có thể thử này

d = dict(locals(), **globals()) 
exec (code, d, d) 
+0

Đây là phản hồi duy nhất mà bạn có thể gửi và nhận lại toàn cầu/người dân địa phương. Câu trả lời tuyệt vời. Sau đó, bạn có thể sử dụng d làm cơ sở truy cập các biến từ mã con. d ['status'] nếu bạn có status = {} trong mã của bạn. Cảm ơn bạn – Dovy

1
my_code = """ 
class A(object): 
    pass 

class B(object): 
    a = A 
""" 

my_code_AST = compile(my_code, "My Code", "exec") 
extra_global = "hi" 
global_env = {} 
exec my_code_AST in global_env 
print "global_env.keys() =", global_env.keys() 
print "B.a =", global_env["B"].a 

in

global_env.keys() = ['__builtins__', 'A', 'B'] 
B.a = <class 'A'> 

Hawkett , bạn nói,

lý do chính tôi muốn sử dụng người dân địa phương như vậy, là để có được tất cả những thứ được xác định trong chuỗi mã, mà không có tất cả các thứ khác mà python đặt trong các hình cầu.

Với exec, nếu globals của bạn không có __builtins__ xác định, exec thêm một mục, __builtins__ để globals của bạn, vì vậy bạn sẽ có được A, B, và __builtins__. __builtins__ chính nó là một từ điển lớn, nhưng nó luôn luôn là một yếu tố tương tự để xóa (miễn là bạn đợi cho đến khi mã của bạn được hoàn thành bằng cách sử dụng nó trước khi bạn xóa nó!). Tài liệu theo exec() here.

Các tài liệu cho eval dưới built in functions nói

Nếu điển globals là hiện tại và thiếu ‘builtins’, các globals hiện tại sẽ được sao chép vào globals trước khi biểu hiện được phân tách.

Nhưng trên thực tế có vẻ như chỉ để sao chép __builtins__ trong

(Và n.b. những gì người khác nói:. Hoặc bộ globals và người dân địa phương cùng hoặc nói exec my_code_AST in global_env mà không có một local_env riêng biệt.)

Các vấn đề liên quan