2012-08-29 14 views
8

Tôi vừa mới học python @ decorator, nó rất tuyệt, nhưng chẳng mấy chốc tôi tìm thấy mã đã sửa đổi của mình phát ra những vấn đề lạ.Chức năng đóng của Python mất quyền truy cập biến ngoài

def with_wrapper(param1): 
    def dummy_wrapper(fn): 
     print param1 
     param1 = 'new' 
     fn(param1) 
    return dummy_wrapper 

def dummy(): 
    @with_wrapper('param1') 
    def implementation(param2): 
     print param2 

dummy() 

tôi gỡ lỗi nó, nó ném ra ngoại lệ tại in param1

UnboundLocalError: local variable 'param1' referenced before assignment 

Nếu tôi loại bỏ param1 = 'new' dòng này, mà không cần bất kỳ hoạt động sửa đổi (liên kết đến đối tượng mới) trên biến từ phạm vi bên ngoài, thói quen này có thể làm việc.

Có phải là tôi chỉ tạo một bản sao của các biến phạm vi bên ngoài, sau đó sửa đổi?


Cảm ơn Delnan, cần thiết phải đóng cửa. câu trả lời có khả năng từ đây: What limitations have closures in Python compared to language X closures?

đang tương tự như:

def e(a): 
    def f(): 
     print a 
     a = '1' 
    f() 
e('2') 

Và cũng điều này dường như biến toàn cầu gây phiền nhiễu trước:

a = '1' 
def b(): 
    #global a 
    print a 
    a = '2' 
b() 

này được cố định bằng cách thêm biểu tượng toàn cầu. Nhưng để đóng cửa, không tìm thấy biểu tượng nào. Cảm ơn unutbu, Python 3 đã cho chúng tôi nonlocal.

Tôi biết từ trên trực tiếp truy cập vào biến bên ngoài là chỉ đọc. nhưng không thoải mái khi thấy biến đọc trước (print var) cũng bị ảnh hưởng.

+0

thể trùng lặp của [gì hạn chế có đóng cửa trong Python so với ngôn ngữ X đóng cửa?] (http://stackoverflow.com/questions/141642/what-limitations-have-closures-in-python-compared-to-language-x-closures) – delnan

+0

Điều này đã hoàn toàn không có gì để làm với trang trí btw. – delnan

+0

Có, điều này xảy ra trong bao đóng. như thế này: def e (a): def f(): in một a = '1' f() e ('123') –

Trả lời

11

Khi Python phân tích một chức năng, nó ghi nhận bất cứ khi nào nó tìm thấy một biến được sử dụng ở phía bên trái của một bài tập, chẳng hạn như

param1 = 'new' 

Nó giả định rằng tất cả các biến như là cục bộ của hàm. Vì vậy, khi bạn đặt trước nhiệm vụ này với

print param1 

một lỗi xảy ra vì Python không có giá trị cho biến cục bộ này vào thời điểm đó báo cáo kết quả in ấn được thực thi.


Trong Python3 bạn có thể sửa lỗi này bằng cách tuyên bố rằng param1 là không cục bộ:

def with_wrapper(param1): 
    def dummy_wrapper(fn): 
     nonlocal param1 
     print param1 
     param1 = 'new' 
     fn(param1) 
    return dummy_wrapper 

Trong python2 bạn phải dùng đến một thủ thuật, chẳng hạn như đi qua param1 bên trong một danh sách (hoặc một số đối tượng có thể thay đổi khác):

def with_wrapper(param1_list): 
    def dummy_wrapper(fn): 
     print param1_list[0] 
     param1_list[0] = 'new' # mutate the value inside the list 
     fn(param1_list[0]) 
    return dummy_wrapper 

def dummy(): 
    @with_wrapper(['param1']) # <--- Note we pass a list here 
    def implementation(param2): 
     print param2 
+0

Và cách tốt để 'sửa chữa' này là gì? – Silox

+1

... đặt câu lệnh 'print' sau bài tập? – kindall

+0

Cảm ơn bạn đã giải thích. Nhưng tôi không có ý tưởng về lý do tại sao param1 = 'new' gán lại param bên ngoài nhưng để tạo một var cục bộ. –

0

Bạn chỉ định param1 trong hàm, làm cho param1 biến cục bộ. Tuy nhiên, nó đã không được chỉ định tại thời điểm bạn đang in nó, vì vậy bạn nhận được một lỗi. Python không quay trở lại tìm kiếm các biến trong phạm vi bên ngoài.

+0

_print param1_ là một ví dụ, trong mã thực, bất kỳ tham chiếu nào khi param1 đều bị cấm khi param1 = xx tồn tại –

1

Nếu bạn muốn chụp một biến từ phạm vi bên ngoài trong Python 2.x thì sử dụng toàn cầu cũng là một tùy chọn (với các điều kiện thông thường - nhưng tiện dụng cho các tình huống tạm thời hoặc mã khám phá).

Trong khi những điều sau đây sẽ ném (giao outer1 trong nội tâm làm cho nó địa phương và do đó vô biên trong điều kiện if):

def outer(): 
    outer1 = 1 
    def inner(): 
     if outer1 == 1: 
      outer1 = 2 
      print('attempted to accessed outer %d' % outer1) 

này sẽ không:

def outer(): 
    global outer1 
    outer1 = 1 
    def inner(): 
     global outer1 
     if outer1 == 1: 
      outer1 = 2 
      print('accessed outer %d' % outer1)