2012-02-13 19 views
93

Tôi đang làm gì sai ở đây?UnboundLocalError trong Python

counter = 0 

def increment(): 
    counter += 1 

increment() 

Mã trên ném một số UnboundLocalError.

+11

@ZeroPiraeus: tại sao bạn búa một câu hỏi với 40k xem +, đó là kết quả đầu tiên cho "UnboundLocalError" trong Google, là bản sao của một câu hỏi gần giống hệt nhau mà bạn đã trả lời thay vì chỉ đăng câu trả lời của bạn * ở đây *? – vaultah

+1

Câu hỏi này và câu hỏi hiện đang được đánh dấu trùng lặp đang được thảo luận trong [Phòng chat Python] (http://chat.stackoverflow.com/transcript/message/34899645#34899645). –

+3

Nhiều câu trả lời ở đây nói để sử dụng 'toàn cục', và mặc dù nó hoạt động, sử dụng các hình cầu có thể sửa đổi thường là _not_ khuyên dùng khi các tùy chọn khác tồn tại. –

Trả lời

97

Python không có khai báo biến, vì vậy nó phải tìm ra các scope của các biến riêng của mình. Nó làm như vậy bằng một quy tắc đơn giản: Nếu có một phép gán cho một biến bên trong một hàm, biến đó được coi là cục bộ. [1] Như vậy, dòng

counter += 1 

ngầm làm counter địa phương để increment(). Tuy nhiên, cố gắng thực hiện dòng này sẽ cố đọc giá trị của biến cục bộ counter trước khi được gán, dẫn đến số UnboundLocalError. [2]

Nếu counter là biến toàn cục, từ khóa global sẽ giúp ích. Nếu increment() là hàm cục bộ và counter biến cục bộ, bạn có thể sử dụng nonlocal bằng Python 3.x.

+2

python 3 tài liệu có [faq page về lý do tại sao-am-i-get-an-unboundlocalerror-khi-biến-giá-có-một-giá trị] (https://docs.python.org/3/faq/programming .html # why-am-i-get-an-unboundlocalerror-khi-the-biến-có-một-giá trị) qua [unboundlocalerror-local-variable-l-tham chiếu-trước-gán-python] (http: // stackoverflow.com/questions/21456739/unboundlocalerror-local-variable-l-referenced-before-assignment-python) – here

+0

Một lưu ý đã bắt được tôi, tôi đã có một biến được khai báo ở đầu tệp mà tôi có thể đọc bên trong một hàm mà không có vấn đề, tuy nhiên để ghi vào một biến mà tôi đã tuyên bố ở trên cùng của tập tin, tôi đã phải sử dụng toàn cầu. – mouckatron

3

Để sửa đổi biến toàn cầu bên trong một hàm, bạn phải sử dụng từ khóa chung.

Khi bạn cố gắng để làm điều này mà không cần dòng

global counter 

bên trong định nghĩa của tăng, một bộ đếm biến địa phương đặt tên được tạo ra để giữ cho bạn từ mucking lên biến truy cập rằng toàn bộ chương trình có thể phụ thuộc vào.

Lưu ý rằng bạn chỉ cần sử dụng toàn cầu khi bạn đang sửa đổi biến; bạn có thể đọc truy cập từ bên trong gia tăng mà không cần báo cáo toàn cầu.

51

Bạn cần phải sử dụng global statement để bạn được điều chỉnh bộ đếm biến toàn cầu, thay vì một biến địa phương:

counter = 0 

def increment(): 
    global counter 
    counter += 1 

increment() 

Nếu phạm vi kèm theo đó counter được định nghĩa trong không phải là phạm vi toàn cầu, trên Python 3.x bạn có thể sử dụng nonlocal statement. Trong tình hình như vậy trên Python 2.x bạn sẽ không có cách nào để chỉ định lại tên không cục bộ counter, vì vậy bạn sẽ cần phải thực hiện counter có thể thay đổi và sửa đổi nó:

counter = [0] 

def increment(): 
    counter[0] += 1 

increment() 
print counter[0] # prints '1' 
+0

Cool .. Nó hoạt động +1 –

+0

Nó hoạt động nhờ. –

2

thử này

counter = 0 

def increment(): 
    global counter 
    counter += 1 

increment() 
0
+0

Trong khi liên kết này có thể trả lời câu hỏi, tốt hơn nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở thành không hợp lệ nếu trang được liên kết thay đổi. - [Từ đánh giá] (/ review/low-quality-posts/19036082) – munk

+0

@munk Cool, bạn nhận ra rằng đây là một liên kết đến một câu trả lời khác trên SO? – Marcin

3

Python có phạm vi từ vựng theo mặc định, có nghĩa là mặc dù phạm vi kèm theo có thể truy cập các giá trị trong phạm vi kèm theo của nó, nó không thể sửa đổi chúng (trừ khi chúng được khai báo chung với từ khóa global).

Đóng cửa liên kết giá trị trong môi trường bao quanh với tên trong môi trường cục bộ. Môi trường cục bộ sau đó có thể sử dụng giá trị bị ràng buộc, và thậm chí gán lại tên đó cho một cái gì đó khác, nhưng nó không thể sửa đổi ràng buộc trong môi trường kèm theo.

Trong trường hợp của bạn, bạn đang cố gắng xử lý counter dưới dạng biến cục bộ chứ không phải giá trị bị ràng buộc. Lưu ý rằng đoạn mã này, mà liên kết với giá trị của x giao trong môi trường kèm theo, hoạt động tốt:

>>> x = 1 

>>> def f(): 
>>> return x 

>>> f() 
1 
11

Để trả lời các câu hỏi trong dòng tiêu đề của bạn, * vâng, có đóng cửa bằng Python, ngoại trừ họ chỉ áp dụng bên trong một hàm, và cả (trong Python 2.x) chúng là chỉ đọc; bạn không thể liên kết lại tên với một đối tượng khác (mặc dù nếu đối tượng có thể thay đổi, bạn có thể sửa đổi nội dung của nó). Trong Python 3.x, bạn có thể sử dụng từ khóa nonlocal để sửa đổi biến đóng.

def incrementer(): 
    counter = 0 
    def increment(): 
     nonlocal counter 
     counter += 1 
     return counter 
    return increment 

increment = incrementer() 

increment() # 1 
increment() # 2 

* Tiêu đề câu hỏi ban đầu được hỏi về đóng cửa bằng Python.

6

Lý do tại sao mã của bạn ném một số UnboundLocalError đã được giải thích rõ trong các câu trả lời khác.

Nhưng có vẻ như với tôi rằng bạn đang cố gắng xây dựng thứ gì đó hoạt động như itertools.count().

Vậy tại sao bạn không thử nó ra và xem nếu nó phù hợp với trường hợp của bạn:

>>> from itertools import count 
>>> counter = count(0) 
>>> counter 
count(0) 
>>> next(counter) 
0 
>>> counter 
count(1) 
>>> next(counter) 
1 
>>> counter 
count(2)