2015-12-23 15 views
21

Các mã sau đặt ra một UnboundLocalError:Thay đổi một biến bên trong một phương pháp với phương pháp khác bên trong nó

def foo(): 
    i = 0 
    def incr(): 
     i += 1 
    incr() 
    print(i) 

foo() 

Có cách nào để thực hiện điều này?

+4

Bạn có thể chuyển 'i' làm đối số. – Maroun

+1

Bạn có thể cập nhật giá trị của 'i' là' i = incr() ' –

+2

@MarounMaroun Judgind từ câu hỏi này, Python cái gì, không có bao đóng? Tuy nhiên, tôi không quen với nó. Hay là vì 'def' định nghĩa một hàm trong không gian tên chung? Tôi chỉ tò mò, và không muốn học Python chỉ vì điều này. :) – hijarian

Trả lời

9

Xem 9.2. Python Scopes and Namespaces:

nếu không có tuyên bố global là trong hiệu ứng - các bài tập cho các tên luôn đi vào phạm vi bên trong nhất.

Ngoài ra:

Các global tuyên bố có thể được sử dụng để chỉ ra rằng các biến đặc biệt sống trong phạm vi toàn cầu và cần được hồi phục đó; tuyên bố nonlocal cho biết rằng các biến cụ thể sống trong một phạm vi kèm theo và phải được khôi phục ở đó.

Bạn có nhiều giải pháp:

  • đèo i như một ✓ luận (Tôi sẽ đi với cái này)
  • Sử dụng nonlocal từ khóa

Lưu ý rằng trong Python2.x bạn có thể truy cập các biến không phải cục bộ nhưng bạn không thể thay đổi chúng.

20

Bạn có thể sử dụng i như một cuộc tranh cãi như thế này:

def foo(): 
    i = 0 
    def incr(i): 
     return i + 1 
    i = incr(i) 
    print(i) 

foo() 
+6

Câu trả lời hay, vì điều này không phụ thuộc vào Python 3, giống như các giải pháp 'nonlocal' khác. –

+2

Tên 'incr' không được chọn cho một hàm trả về nhiều hơn đối số của nó mà không có bất kỳ tác dụng phụ nào. –

+0

Tên của phiên bản chức năng này của 'incr' có thể là' succ' :) –

4

Trong Python, int là không thay đổi. Vì vậy, bạn có thể đặt int của bạn trong một đối tượng có thể thay đổi.

def foo(): 
    i = 0 
    obj = [i] 
    def incr(obj): 
     obj[0]+=1 
    incr(obj) 
    print(obj[0]) 

foo() 
+1

Với phương pháp này, không cần phải chuyển 'obj' làm đối số thành' incr'; nó hoạt động tốt như một đóng cửa. – tsbertalan

2

Bạn có thể tạo i toàn cầu và sử dụng nó.

i = 0 
def foo(): 
    def incr(): 
     global i 
     i += 1 
    incr() 
    print(i) 

foo() 

nhưng cách được ưa thích nhất là phải vượt qua i như một param để incr

def foo(): 
    i = 0 
    def incr(arg): 
     arg += 1 
     return arg 
    i = incr(i) 
    print(i) 

foo() 
+0

@BurhanKhalid về thử nghiệm nó dường như làm việc, xin vui lòng điểm nếu tôi bị mất bất cứ điều gì. – anand

6

dân đã trả lời câu hỏi của bạn nhưng không ai dường như để giải quyết lý do tại sao chính xác điều này xảy ra.

Các mã sau đặt ra một UnboundLocalError

Vì vậy, tại sao ?Hãy lấy một câu trích dẫn từ số FAQ:

Khi bạn gán một biến trong phạm vi, biến đó trở thành phạm vi địa phương và biến bất kỳ biến được đặt tên tương tự nào trong phạm vi bên ngoài.

Bên trong hàm lồng nhau bạn đang thực hiện một nhiệm vụ với toán tử +=. Điều này có nghĩa là i += 1 sẽ xấp xỉ thực hiện i = i + 1 (từ góc độ ràng buộc). Kết quả là i trong biểu thức i + 1 sẽ được tìm kiếm trong phạm vi cục bộ (vì nó được sử dụng trong câu lệnh gán) cho hàm incr nơi không tìm thấy nó, dẫn đến UnboundLocalError: reference before assignment.

Có nhiều cách để bạn có thể giải quyết này và python 3 là tao nhã hơn trong cách tiếp cận, bạn có thể mất hơn python 2.

Python 3 nonlocal:

Các nonlocal tuyên bố nói với Python để tìm kiếm một tên trong phạm vi kèm theo (như vậy trong trường hợp này, trong phạm vi chức năng foo()) để tham khảo và tên:

def foo(): 
    i = 0 
    def incr(): 
     nonlocal i 
     i +=1 
    incr() 
    print(i) 

Function attributes Python 2.x and 3.x:

Hãy nhớ rằng các hàm là đối tượng lớp đầu tiên, vì vậy chúng có thể lưu trữ trạng thái. Sử dụng các thuộc tính chức năng để truy cập và thay đổi trạng thái chức năng, điều tốt với phương pháp này là nó hoạt động trên tất cả trăn và không yêu cầu câu hỏi global, nonlocal.

def foo(): 
    foo.i = 0 
    def incr(): 
     foo.i +=1 
    incr() 
    print(foo.i) 

The global Statement (Python 2.x, 3.x):

Thực sự là xấu nhất của bó, nhưng được công việc làm:

i = 0 
def foo(): 
    def incr(): 
     global i 
     i += 1 
    incr() 
    print(i) 

foo() 

Các tùy chọn thông qua một cuộc tranh cãi với chức năng nhận được cùng kết quả nhưng nó không liên quan đến việc đột biến phạm vi kèm theo theo nghĩa nonlocalglobal là và giống như các thuộc tính hàm được trình bày. Nó đang tạo ra một biến địa phương mới trong hàm inc và sau đó gắn lại tên với i với i = incr(i) nhưng, nó thực sự có được công việc làm.

0

bạn cũng có thể sử dụng một hàm lambda:

def foo(): 
    i=0 
    incr = lambda x: x+1 
    print incr(i) 

foo() 

Tôi nghĩ rằng mã là sạch hơn theo cách này

0

chức năng đơn giản thuộc tính sẽ không làm việc trong trường hợp này.

>>> def foo(): 
...  foo.i = 0 
...  def incr(): 
...   foo.i +=1 
...  incr() 
...  print(foo.i) 
... 
>>> 
>>> 
>>> foo() 
1 
>>> foo() 
1 
>>> foo() 
1 

Bạn lại gán foo.i của bạn để 0 cho mỗi cuộc gọi của foo. Tốt hơn nên sử dụng hassattr. Nhưng mã trở nên phức tạp hơn.

>>> def foo(): 
...  if not hasattr(foo, 'i'): 
...   foo.i = 0 
...  def incr(): 
...   foo.i += 1 
...   return foo.i 
...  i = incr() 
...  print(i) 
... 
>>> 
>>> 
>>> foo() 
1 
>>> foo() 
2 
>>> foo() 
3 

Ngoài ra bạn có thể thử ý tưởng này:

>>> def foo(): 
...  def incr(): 
...   incr.i += 1 
...   return incr.i 
...  incr.i = 0 
...  return incr 
... 
>>> a = foo() 
>>> a() 
1 
>>> a() 
2 
>>> a() 
3 
>>> a() 
4 

Có thể nó là tiện dụng hơn để quấn bạn incr vào trang trí.

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