2012-01-19 37 views
25

Đối với Python 2,7 mã sau:Gán cho biến từ chức năng cha mẹ: "biến địa phương tham chiếu trước khi chuyển nhượng"

#!/usr/bin/python 

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(): 
     c += 3 
     print "funcB", c 

    def funcC(): 
     print "funcC", c 

    print "c", c 
    funcB() 
    c += 2 
    funcC() 
    c += 2 
    funcB() 
    c += 2 
    funcC() 
    print "end" 

funcA() 

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

File "./a.py", line 9, in funcB 
    c += 3 
UnboundLocalError: local variable 'c' referenced before assignment 

Nhưng khi tôi nhận xét ra dòng c += 3 trong funcB, tôi nhận được kết quả sau:

funcA 
c 0 
funcB 0 
funcC 2 
funcB 4 
funcC 6 
end 

Không phải là c được truy cập trong cả hai trường hợp += trong funcB= trong funcC? Tại sao nó không ném lỗi cho một nhưng không cho người khác?

Tôi không có lựa chọn tạo c biến toàn cầu và sau đó khai báo global c trong funcB. Dù sao, điểm không phải là để có được c tăng lên trong funcB nhưng tại sao nó ném lỗi cho funcB và không cho funcC trong khi cả hai đang truy cập một biến đó là địa phương hoặc toàn cầu.

+0

chuyển c thành thông số ... – joaquin

+0

tôi đã sửa đổi mã một chút bây giờ là phiên bản đúng của câu hỏi. – crk

+0

Liên kết này cũng có một số thông tin, http://docs.python.org/faq/programming.html # why-am-i-get-an-unboundlocalerror-when-the-variable-has-a-giá trị –

Trả lời

49

gì bạn đang nhìn thấy ở đây là sự khác biệt giữa việc truy cập và gán các biến. Trong Python 2.x bạn chỉ có thể gán cho các biến trong phạm vi trong cùng hoặc phạm vi toàn cục (sau này được thực hiện bằng cách sử dụng câu lệnh chung). Bạn có thể truy cập các biến trong bất kỳ phạm vi bao quanh nào, nhưng bạn không thể truy cập một biến trong một phạm vi kèm theo và sau đó gán cho nó trong phạm vi bên trong hoặc toàn cục nhất. Điều này có nghĩa là nếu có bất kỳ gán cho một tên bên trong một hàm, tên đó phải được xác định trong phạm vi bên trong nhất trước khi tên được truy cập (trừ khi câu lệnh chung được sử dụng).Trong mã của bạn dòng c += 3 là về cơ bản tương đương như sau:

tmp = c 
c = tmp + 3 

Bởi vì có một bài tập để c trong hàm, mỗi lần xuất hiện khác của c trong chức năng đó sẽ chỉ tìm trong phạm vi địa phương cho funcB . Đây là lý do bạn thấy lỗi, bạn đang cố gắng truy cập c để nhận giá trị hiện tại cho số +=, nhưng trong phạm vi địa phương c chưa được xác định.

Trong Python 3 bạn có thể giải quyết vấn đề này bằng cách sử dụng nonlocal statement, cho phép bạn gán cho các biến không nằm trong phạm vi hiện tại, nhưng cũng không nằm trong phạm vi toàn cục.

Mã của bạn sẽ giống như thế này, với một dòng tương tự ở đầu funcC:

def funcB(): 
     nonlocal c 
     c += 3 
     ... 

Trong Python 2.x đây không phải là một lựa chọn, và cách duy nhất bạn có thể thay đổi giá trị của một biến phi địa phương là nếu nó có thể thay đổi được.

Cách đơn giản nhất để làm điều này là để quấn giá trị của bạn trong danh sách, và sau đó chỉnh sửa và truy cập vào phần tử đầu tiên của danh sách đó trong mọi nơi mà bạn có trước đây chỉ được sử dụng tên biến:

def funcA(): 
    print "funcA" 
    c = [0] 
    def funcB(): 
     c[0] += 3 
     print "funcB", c[0] 

    def funcC(): 
     c[0] = 5 
     print "funcC", c[0] 

    print "c", c[0] 
    funcB() 
    funcC() 
    funcB() 
    funcC() 
    print "end" 

funcA() 

... và đầu ra:

funcA 
c 0 
funcB 3 
funcC 5 
funcB 8 
funcC 5 
end 
+4

Thật là một lời giải thích tốt! Góc tối đen của Python trong việc xác định phạm vi bởi ** sự tồn tại ** của bài tập ** ở bất cứ đâu ** trong một hàm (nhiều vào cuối trong trường hợp của tôi) chỉ khiến tôi phát điên khi tôi tìm kiếm lời giải thích trong các câu trả lời SO khác internet bên ngoài. Và lái xe thậm chí còn điên rồ hơn khi tôi đã gặp phải một số ví dụ ngược lại ** không ** tạo ra lỗi chết tiệt, với các giải thích rằng việc truy cập các hình cầu là tất cả đều ổn trong Python! Cuối cùng, đã chứng ngộ ở đây!^_^ –

6

Isn't 'c' being accessed in both cases of '+=' in funcB and '=' in funcC?

Không, funcC tạo biến mới, còn được gọi là c. = là khác nhau về mặt này từ +=.

Để có được hành vi của bạn (có thể là) muốn, quấn biến lên trong một danh sách phần tử duy nhất:

def outer(): 
    c = [0] 
    def inner(): 
     c[0] = 3 
    inner() 
    print c[0] 

sẽ in 3.

Chỉnh sửa: Bạn sẽ muốn chuyển c làm đối số. Python 2 không có cách nào khác, AFAIK, để có được hành vi mong muốn. Python 3 giới thiệu từ khóa nonlocal cho những trường hợp này.

0

Hãy thử điều này:

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(c): 
     c += 3 
     print "funcB", c 

    def funcC(c): 
     c = 5 
     print "funcC", c 

    print "c", c 
    funcB(c) 
    funcC(c) 
    funcB(c) 
    funcC(c) 
    print "end" 

funcA() 

Và nếu bạn muốn ghi nhớ giá trị c thì:

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(c): 
     c += 3 
     print "funcB", c 
     return c 

    def funcC(c): 
     c = 5 
     print "funcC", c 
     return c 

    print "c", c 
    c = funcB(c) 
    c = funcC(c) 
    c = funcB(c) 
    c = funcC(c) 
    print "end" 

funcA() 

rằng sẽ sản xuất:

funcA 
c 0 
funcB 3 
funcC 5 
funcB 8 
funcC 5 
end 

C:\Python26\ 
2

1) không phải là c được truy cập trong cả hai trường hợp của += trong funcB và = trong funcC?

Không, bởi vì c += 3 là giống như:

c = c + 3 
    ^
    | 
and funcB does not know what this c is 

2) Tôi không có sự lựa chọn làm c một biến toàn cầu và sau đó tuyên bố global c trong funcB.

Xin đừng làm điều đó, chỉ cần thay đổi:

def funcB(): 

với:

def funcB(c): 

và gọi funcB(c) sau trong mã của bạn.

Lưu ý: Bạn cũng nên cosider để xác định funcBfuncC ngoài funcA

0

Một workaround bẩn, trong đó, tuy nhiên, không yêu cầu bạn phải làm cho c toàn cầu. Mọi thứ đều giống nhau, nhưng:

def funcB(): 
    globals()['c'] += 3 
    print "funcB", c 
+0

tốt, toàn bộ điều theo cách nó được hỏi không phải là rất thanh lịch, nhưng chỉ để làm cho nó hoạt động - cách tiếp cận này hoạt động. – dmytro

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