2010-03-25 28 views
8

tại sao công việc này:lỗi Phạm vi trong việc đóng cửa đệ quy

def function1():                            
     a = 10                              
     def function2(): 
      print a 
     function2() 

nhưng điều này không:

def function1(): 
    a = 10 
    def function2(): 
     print a 
     a -= 1 
     if a>0: 
      function2() 
    function2() 

tôi nhận được lỗi này:

UnboundLocalError: local variable 'a' referenced before assignment 

Trả lời

13

Các lỗi dường như không thể rất mô tả vấn đề gốc. Mike giải thích các thông điệp nhưng điều đó không giải thích nguyên nhân gốc rễ.

Vấn đề thực tế là trong python bạn không thể gán cho đóng trên các biến. Vì vậy, trong function2 'a' là chỉ đọc. Khi bạn gán cho nó, bạn tạo một biến mới, như Mike chỉ ra, bạn đọc trước khi bạn viết.

Nếu bạn muốn gán cho vào biến bên ngoài từ phạm vi bên trong bạn có để lừa như vậy:

def function1(): 
    al = [10] 
    def function2(): 
     print al[0] 
     al[0] -= 1 
     if al[0]>0: 
      function2() 
    function2() 

Vì vậy, al là không thay đổi nhưng nội dung của nó không và bạn có thể thay đổi chúng mà không cần tạo một biến mới.

+2

Thật vậy, đây là điểm then chốt trong việc thiết kế chức năng này — bạn không thể gán cho phạm vi không phải địa phương. (Lưu ý: 'al' là * có thể thay đổi *; đó là lý do tại sao nó hoạt động.) –

+2

Tôi nghĩ điều quan trọng là vì sự rõ ràng, để phân biệt giữa biến al và các giá trị al chứa. Nó luôn luôn trở lại với con trỏ cho tôi vì vậy hãy để tôi nói điều này; bạn không thể làm cho al điểm đến một danh sách mới nhưng bạn có thể thay đổi nội dung của danh sách mà al trỏ đến. al -> [v1, v2, v3] al không thể thay đổi nhưng v1, v2 và v3 có thể thay đổi. Mike là hoàn toàn chính xác rằng điều này làm cho al mutable bởi vì trong thuật ngữ của chúng tôi al * là * danh sách không phải là con trỏ vào danh sách. – charlieb

+0

+1 câu trả lời rất hay. –

2

Trong không làm việc đoạn , bạn chỉ định cho a khi bạn nói "a -= 1". Do đó, bên trong function2, a là địa phương trong phạm vi đó, không được lấy phạm vi bao quanh. Đóng cửa của Python là từ vựng - nó không tự động tìm kiếm a trong khung của function2 và nếu nó chưa được gán đi và tìm nó trong khung của function1.

Lưu ý rằng điều này không phụ thuộc vào sự đệ quy hoặc sử dụng bao đóng. Hãy xem xét ví dụ về chức năng này

def foo(): 
    print a 
    a = 4 

Gọi điện thoại cũng sẽ giúp bạn một số UnboundLocalError. (Nếu không có a = 4, nó sẽ sử dụng toàn cầu a hoặc nếu không có, hãy tăng NameError.) Vì a có khả năng được chỉ định trong phạm vi đó, đó là địa phương.


Nếu tôi được thiết kế chức năng này, tôi có thể sử dụng một cách tiếp cận giống như

def function1(): 
    a = 10 
    def function2(a=a): 
     print a 
     a -= 1 
     if a > 0: 
      function2(a) 
    function2() 

(hoặc tất nhiên for a in xrange(10, -1, -1): print a ;-))

5

Cần lưu ý rằng đây là lỗi cú pháp trong Python. Bản thân Python (ở cấp độ bytecode) có thể gán cho các biến này tốt; chỉ đơn giản là không có cú pháp trong 2.x để chỉ ra rằng bạn muốn làm như vậy. Nó giả định rằng nếu bạn gán cho một biến trong một mức làm tổ, bạn có nghĩa là nó sẽ là một biến cục bộ cho nó.

Đây là một thiếu sót lớn; có thể gán cho các bao đóng là nền tảng. Tôi đã làm việc xung quanh điều này với hack của charlieb nhiều lần.

Python 3 sửa lỗi này với "không cục bộ" từ khóa rất ngượng nghịu tên:

def function1(): 
    a = 10 
    def function2(): 
     nonlocal a 
     print(a) 
     a -= 1 
     if a>0: 
      function2() 
    function2() 
function1() 

Nó rất nghèo mà cú pháp này chỉ có sẵn trong 3.x; hầu hết mọi người đang bị mắc kẹt trong 2.x, và phải tiếp tục sử dụng hacks để làm việc xung quanh vấn đề này.Điều này rất cần phải được chuyển về 2.x.

http://www.python.org/dev/peps/pep-3104/

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