2013-02-16 37 views
7

Tôi đã xác định ba hàm sẽ thay đổi biến toàn cục x.Sự khác biệt giữa "toàn cầu" và "nhập __main__"

def changeXto1(): 
    global x 
    x = 1 

def changeXto2(): 
    from __main__ import x 
    x = 2 

def changeXto3(): 
    import __main__ 
    __main__.x = 3 

x = 0 
print x 
changeXto1() 
print x 
changeXto2() 
print x 
changeXto3() 
print x 

Nó cho kết quả:

0 
1 
1 
3 

changeXto1 sử dụng các báo cáo toàn cầu bình thường. Kết quả như mong đợi x == 1. changeXto2 sử dụng from __main__ import để giải quyết x. Điều này không hoạt động. Sau đó, x vẫn là 1. changeXto3 sử dụng import main để giải quyết x qua __main__.x. Kết quả sau đó là 3 như mong đợi.

Tại sao không from __main__ import làm việc trong changeXto2, trong khi import __main__ đang làm việc trong changeXto3? Tại sao chúng ta cần một câu lệnh chung trong Python nếu chúng ta có thể giải quyết các biến toàn cầu cũng với mô-đun __main__?

+0

changeXto2 chỉ đặt một biến địa phương: http://www.saltycrane.com/blog/2008/01/python-variable- phạm vi ghi chú/ –

+0

Tôi không thấy bất kỳ điều gì về việc nhập mô-đunnhập vào đó. –

Trả lời

10

Điều này liên quan đến cách Python dịch mã của bạn sang bytecode (bước biên dịch).

Khi biên dịch một hàm, Python xử lý tất cả các biến được gán dưới dạng biến cục bộ và thực hiện tối ưu hóa để giảm số lượng tra cứu tên mà nó sẽ phải làm. Mỗi biến cục bộ được gán một chỉ mục và khi hàm được gọi là giá trị của chúng sẽ được lưu trữ trong một mảng cục bộ ngăn xếp được chỉ mục theo chỉ mục. Trình biên dịch sẽ phát ra mã vạch LOAD_FASTSTORE_FAST để truy cập biến.

Cú pháp global chỉ thay cho trình biên dịch ngay cả khi biến được gán giá trị, nó không được coi là biến cục bộ, không được gán chỉ mục. Thay vào đó, nó sẽ sử dụng opcode LOAD_GLOBALSTORE_GLOBAL để truy cập biến. Những opcode chậm hơn kể từ khi họ sử dụng tên để làm một tra cứu trong có thể nhiều từ điển (người dân địa phương, globals).

Nếu biến chỉ được truy cập để đọc giá trị, trình biên dịch luôn phát ra LOAD_GLOBAL vì nó không biết liệu biến đó có phải là biến cục bộ hay toàn cục hay không, và do đó giả sử nó là toàn cầu.Vì vậy, trong chức năng đầu tiên của bạn, sử dụng global x thông báo cho trình biên dịch mà bạn muốn nó xử lý quyền truy cập ghi vào x làm văn bản cho biến toàn cầu thay vì biến cục bộ. Opcodes cho chức năng làm cho nó rõ ràng:

>>> dis.dis(changeXto1) 
    3   0 LOAD_CONST    1 (1) 
       3 STORE_GLOBAL    0 (x) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE   

Trong ví dụ thứ ba của bạn, bạn nhập các module __main__ vào một biến địa phương tên là __main__ và sau đó gán cho lĩnh vực x của nó. Vì mô-đun là đối tượng lưu trữ tất cả các ánh xạ cấp cao nhất làm trường, bạn đang gán cho biến x trong mô-đun __main__. Và như bạn thấy, các trường mô-đun __main__ trực tiếp ánh xạ tới các giá trị trong từ điển globals() vì mã của bạn được xác định trong mô-đun __main__. Opcodes chứng minh rằng bạn không truy cập trực tiếp x:

>>> dis.dis(changeXto3) 
    2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    0 (None) 
       6 IMPORT_NAME    0 (__main__) 
       9 STORE_FAST    0 (__main__) 

    3   12 LOAD_CONST    2 (3) 
      15 LOAD_FAST    0 (__main__) 
      18 STORE_ATTR    1 (x) 
      21 LOAD_CONST    0 (None) 
      24 RETURN_VALUE   

Ví dụ thứ hai là thú vị. Vì bạn gán một giá trị cho biến số x, trình biên dịch giả sử nó là một biến cục bộ và thực hiện tối ưu hóa. Sau đó, from __main__ import x sẽ nhập mô-đun __main__ và tạo liên kết mới giá trị x trong mô-đun __main__ với biến cục bộ có tên x. Điều này luôn xảy ra, from ${module} import ${name} chỉ cần tạo một ràng buộc mới cho không gian tên hiện tại. Khi bạn gán giá trị mới cho biến x bạn chỉ cần thay đổi ràng buộc hiện tại, không phải ràng buộc trong mô-đun __main__ không liên quan (mặc dù nếu giá trị là có thể thay đổi được và bạn thay đổi nó, thay đổi sẽ hiển thị qua tất cả các ràng buộc). Dưới đây là các opcodes:

>>> dis.dis(f2) 
    2   0 LOAD_CONST    1 (-1) 
       3 LOAD_CONST    2 (('x',)) 
       6 IMPORT_NAME    0 (__main__) 
       9 IMPORT_FROM    1 (x) 
      12 STORE_FAST    0 (x) 
      15 POP_TOP    

    3   16 LOAD_CONST    3 (2) 
      19 STORE_FAST    0 (x) 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE   

Một cách tốt để suy nghĩ về việc này là bằng Python tất cả các nhiệm vụ có tính bắt buộc tên cho một giá trị trong một cuốn từ điển, và dereference chỉ được thực hiện một tra cứu từ điển (đây là một xấp xỉ thô , nhưng khá gần với mô hình khái niệm). Khi thực hiện obj.field, khi đó bạn đang tìm kiếm từ điển ẩn của obj (có thể truy cập qua obj.__dict__) cho khóa "field".

Khi bạn có một tên biến thường, sau đó nó được tra cứu trong từ điển locals(), sau đó từ điển globals() nếu nó khác nhau (chúng giống nhau khi mã được thực thi ở cấp mô-đun). Đối với một nhiệm vụ, nó luôn luôn đặt ràng buộc trong từ điển locals(), trừ khi bạn tuyên bố rằng bạn muốn truy cập toàn cầu bằng cách thực hiện global ${name} (cú pháp này cũng hoạt động ở cấp cao nhất).

Vì vậy, dịch chức năng của bạn, điều này gần như là nếu bạn đã viết:

# NOTE: this is valid Python code, but is less optimal than 
# the original code. It is here only for demonstration. 

def changeXto1(): 
    globals()['x'] = 1 

def changeXto2(): 
    locals()['x'] = __import__('__main__').__dict__['x'] 
    locals()['x'] = 2 

def changeXto3(): 
    locals()['__main__'] = __import__('__main__') 
    locals()['__main__'].__dict__['x'] = 3 
+0

Cảm ơn bạn vì câu trả lời rất toàn diện đó. Tôi luôn luôn nghĩ rằng 'mô-đun nhập khẩu' và' từ nhập khẩu mô-đun' là giống nhau ngoại trừ các biến được gọi khác nhau. Tôi thấy rằng có sự khác biệt, khi biến mới được tạo bằng cách sử dụng 'từ nhập'. Biến trong mô-đun ban đầu giữ nguyên, nhưng biến trong không gian tên cục bộ mới được tạo và liên kết với tên cục bộ. – Holger

8

Tại sao không from __main__ import làm việc trong changeXto2, trong khi import __main__ đang làm việc tại changeXto3?

Nó hoạt động tốt, nó chỉ không làm những gì bạn muốn. Nó sao chép tên và giá trị vào không gian tên cục bộ thay vì có mã truy cập không gian tên __main__.

Tại sao chúng ta cần một câu lệnh chung trong Python nếu chúng ta có thể giải quyết các biến toàn cầu cũng với mô-đun __main__?

Vì chúng chỉ thực hiện tương tự khi mã của bạn đang chạy trong __main__. Nếu bạn đang chạy, giả sử, othermodule sau khi nhập, thì __main__ sẽ tham chiếu đến tập lệnh chính và không phảiothermodule.

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