2012-06-22 29 views
9

Tôi mong đợi đoạn mã nhỏ này để in "Tại sao tính năng này không hoạt động?" Ai đó có thể giúp tôi hiểu tại sao điều này không hoạt động như tôi mong đợi? Tôi đang sử dụng Python 2.6, nếu điều này quan trọng.Đóng cửa Python + kỳ lạ toàn cầu

class WhyDoesntThisWork(object): 
    def outer(self): 
    acc = '' 
    def inner(msg): 
     global acc 
     acc = acc + msg 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
WhyDoesntThisWork().outer() 
  • Nếu tôi bao gồm báo cáo kết quả global tôi nhận được một NameError: global name 'acc' is not defined .
  • Nếu tôi không bao gồm tuyên bố global tôi nhận được UnboundLocalError: local variable 'acc' referenced before assignment.
+4

'toàn cầu' không hoạt động với bao đóng. Bạn sẽ cần 'nonlocal' từ python 3 để thay thế. –

+1

Sử dụng một biến thể thay thế (không có từ khóa chung); 'acc = []', 'acc.append', v.v. –

+0

Được rồi, do đó, sử dụng công cụ có thể thay đổi (' acc = [] '). Bạn có thể giải thích lý do tại sao lại quan trọng ... chỉ vì chuyện vặt sau này? – sholsapp

Trả lời

8

Tôi không biết tại sao nhiều nhận xét ở trên chứa câu trả lời chính xác và không ai dám viết câu trả lời thực sự, vì vậy tôi sẽ thực hiện theo cách này.

class ThisWorksNow(object): 
    def outer(self): 
    acc = [] 
    def inner(msg): 
     acc.append(msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print "".join(acc) 
ThisWorksNow().outer() 

Sự khác biệt là gì?

Gán tên cho đối tượng nằm trong phần đóng không hoạt động trong Python 2.x, vì từ khóa Py3 nonlocal bị thiếu, vì vậy chúng tôi phải tìm giải pháp thay thế.

Nếu chúng ta phải giữ hằng số ràng buộc tên-đối tượng, chúng ta phải thay đổi một thứ khác. Trong trường hợp này, nó là đối tượng, mà chúng ta thêm nội dung được thêm vào.

Dòng print không phải là rất thanh lịch; có thể một đối tượng in nội dung của nó được nối với nhau có thể phù hợp hơn.

class StringBuilder(list): # class name stolen from Java 
    def __str__(self): 
     """this makes the object printable in a way which represents the concatenated string""" 
     return "".join(self) 
    @property 
    def string(self): 
     """this gives us a property which represents the concatenated string""" 
     return "".join(self) 
# use whatever suits you better, one or both 

Với điều này, chúng ta có thể làm điều đó:

class ThisWorksNow(object): 
    def outer(self): 
    acc = StringBuilder() 
    def inner(msg): 
     acc.append(msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
    print acc.string # depending on what you take above 
ThisWorksNow().outer() 

Chỉnh sửa (thêm): Tại sao global không hoạt động?

Chúng tôi cũng có thể đạt được điều này với global, với 2 nhược điểm.

  1. acc sẽ phải được thực hiện trên toàn cầu trên cả hai nơi chúng ta sử dụng nó

    class WhyDoesntThisWork(object): 
        def outer(self): 
        global acc 
        acc = '' 
        def inner(msg): 
         global acc 
         acc = acc + msg 
    

    Dưới đây chúng tôi "nâng đỡ" cả acc lần xuất hiện để "global" cấp.

  2. acc có thể được sửa đổi từ bên ngoài.

    Nếu chúng tôi làm global acc ở một nơi khác hoặc chúng tôi sử dụng acc ở cấp mô-đun, quy trình của chúng tôi có thể bị giả mạo. Điều này nên tránh.

+0

Vui lòng làm rõ. Thay vì chỉ nhận thấy rằng từ khóa 'nonlocal' sẽ cho phép điều này hoạt động, hãy giải thích cơ chế về những gì xảy ra khi sử dụng từ khoá 'global' ở vị trí đó. Từ khoá 'global' bị bỏ qua bởi thông dịch viên, v.v. – wberry

+0

Tôi nghĩ là tôi thấy. Điều này ngụ ý rằng từ khóa 'global' thực sự chỉ áp dụng cho các biến được xác định ở cấp mô-đun; và việc sử dụng 'global' trong một đóng cửa không phải là một vấn đề trong chính nó, như một số nhà bình luận đã tuyên bố. – wberry

+0

Nó không phải là một vấn đề lớn, nhưng nó là ô uế, vì nó gây ô nhiễm không gian tên cấp mô-đun và có thể dẫn đến va chạm. – glglgl

0

Bạn có thể tạo một lớp đóng trợ giúp như sau để đánh dấu rõ ràng biến là đóng. Không chắc chắn nếu một tiện ích như vậy đã tồn tại.

class closure: 
    def __init__(self, val = None): 
     self.val = val 
    def __call__(self, val = None): 
     if val: 
     self.val = val 
     return self.val 
class WhyDoesntThisWork(object): 
    def outer(self): 
    acc = closure('') 
    def inner(msg): 
     acc(acc() + msg) 
    inner("Why doesn't") 
    inner(" this work?") 
    print acc 
WhyDoesntThisWork().outer()