2011-07-26 38 views
6

Có một cái nhìn tại mã này:Python và đóng cửa biến

def closure(): 
    value = False 

    def method_1(): 
     value = True 

    def method_2(): 
     print 'value is:', value 

    method_1() 
    method_2() 

closure() 

Tôi mong chờ nó để in 'Giá trị là: True' nhưng nó không. Tại sao điều này và giải pháp là gì?

+0

Chuỗi này là một ví dụ hoàn hảo về lý do tại sao năm phút chỉnh sửa miễn phí đôi khi không tốt. – agf

+0

@agf: Tại sao nó xấu? – Cameron

Trả lời

16

Điều này xảy ra vì method_1 có phạm vi địa phương riêng của nó, nơi có thể khai báo biến. Python thấy value = True và cho rằng bạn đang tạo biến mới có tên value, địa phương là method_1.

Lý do Python làm điều này là để tránh gây ô nhiễm cho người dân địa phương của phạm vi bên ngoài với các biến từ một hàm bên trong. (Bạn sẽ không muốn các bài tập ở các hàm bình thường, mô-đun cấp để tạo ra các biến toàn cục được tạo ra!)

Nếu bạn không gán cho value, thì Python sẽ tìm kiếm các phạm vi bên ngoài tìm biến đó (để đọc biến hoạt động như mong đợi, như được minh họa bởi số method_2) của bạn.

Một cách để làm được việc này là bằng cách sử dụng một đối tượng có thể thay đổi thay vì assigment:

result = { 'value': False } 

def method_1(): 
    result['value'] = True 

Trong Python 3, nonlocal statement (xem thêm docs) đã được bổ sung cho chính xác kịch bản này:

def method_1(): 
    nonlocal value 
    value = True # Works as expected -- assigns to `value` from outer scope 
2

Trong method_1, Python giả định (khá hợp lý!) Rằng value là biến cục bộ. Bất cứ khi nào bạn gán cho một tên biến bên trong một hàm, nó được giả định rằng tên biến đó là một biến cục bộ mới. Nếu bạn muốn nó là global, thì bạn phải khai báo nó là global, và nếu bạn muốn nó là "nonlocal", trong Python 3, bạn có thể khai báo nó nonlocal, nhưng trong Python 2, bạn phải làm điều gì đó xấu hơn: lưu trữ giá trị trong vùng chứa. Điều đó tránh phải gán lại tên biến, và do đó tránh được sự mơ hồ về phạm vi.

def method_1_global(): 
    global value 
    value = True 

def method_1_nonlocal_P3(): 
    nonlocal value 
    value = True 

value = [False] 
def method_1_nonlocal_P2(): 
    value[0] = True 
+0

Lưu ý rằng với 'toàn cầu', nó không phải là một phần của việc đóng cửa nữa và nó có thể bị rối tung lên ở nơi khác trong chương trình. – agf

+0

@agf - khá như vậy; Tôi chỉ đang cố gắng giải thích các quy tắc phạm vi theo cách chung nhất có thể. – senderle

2

Khi bạn chỉ định cho một biến, nó giả định biến là phạm vi cục bộ. Vì vậy, value trong method_1 không phải là value trong closure.

Nếu bạn muốn điều này hoạt động trên Python 3, hãy thêm một dòng vào method_1: nonlocal value.

Mở Python 2,

def closure(): 
    value = [False] 

    def method_1(): 
     value[0] = True 

    def method_2(): 
     print 'value is:', value 

    method_1() 
    method_2() 

closure() 

là một trong những có thể làm việc xung quanh.

2

Điều này là do bạn không thực sự sửa đổi biến đóng trên - bạn đang che dấu nó bằng một biến mới có cùng tên. Trong python 2.x không có cách nào dễ dàng xung quanh điều này, đó là lý do tại sao các từ khóa nonlocal đã được thêm vào trong python 3.

Điều này có thể được làm việc xung quanh, tuy nhiên, sử dụng các loại có thể thay đổi như danh sách và từ điển. Có một số good example in this answer.

1

Để tránh điều này, bạn có thể sử dụng danh sách.

value = [False] 

def method_1(): 
    value[0] = True 

gì Python sao bây giờ đang tìm kiếm ở mức độ cao hơn về phạm vi như giá trị không có sẵn trong các biến địa phương. Vì giá trị là danh sách và Python tham chiếu biến này dưới dạng biến toàn cầu liên quan đến * method_1 *, bạn có thể coi giá trị vì nó là một danh sách.