2011-01-31 22 views
68

Tôi có đoạn mã sau bên trong một hàm:Python đóng cửa: Viết thư cho biến trong phạm vi mẹ

stored_blocks = {} 
def replace_blocks(m): 
    block = m.group(0) 
    block_hash = sha1(block) 
    stored_blocks[block_hash] = block 
    return '{{{%s}}}' % block_hash 

num_converted = 0 
def convert_variables(m): 
    name = m.group(1) 
    num_converted += 1 
    return '<%%= %s %%>' % name 

fixed = MATCH_DECLARE_NEW.sub('', template) 
fixed = MATCH_PYTHON_BLOCK.sub(replace_blocks, fixed) 
fixed = MATCH_FORMAT.sub(convert_variables, fixed) 

Thêm yếu tố để stored_blocks hoạt động tốt, nhưng tôi không thể tăng num_converted trong subfunction thứ hai:

UnboundLocalError: local variable 'num_converted' referenced before assignment

Tôi có thể sử dụng global nhưng các biến toàn cầu rất xấu và tôi thực sự không cần biến đó thành toàn cục.

Vì vậy, tôi tò mò làm thế nào tôi có thể ghi vào một biến trong phạm vi chức năng của phụ huynh. nonlocal num_converted có thể thực hiện công việc, nhưng tôi cần một giải pháp hoạt động với Python 2.x.

+4

Trái ngược với niềm tin khá phổ biến (đánh giá theo loại câu hỏi này) 'def' không phải là từ khóa duy nhất định nghĩa một không gian tên: còn có' lớp'. –

Trả lời

71

Vấn đề:.. Điều này là do quy tắc Phạm vi của Python là điên Sự hiện diện của toán tử gán += đánh dấu mục tiêu, num_converted, làm địa phương cho phạm vi chức năng kèm theo và không có cách âm nào trong Python 2.x để chỉ truy cập một mức độ phạm vi từ đó. Chỉ từ khóa global mới có thể nâng các tham chiếu biến đổi ra khỏi phạm vi hiện tại và sẽ đưa bạn thẳng lên đầu.

Khắc phục: Biến num_converted thành một mảng phần tử đơn.

num_converted = [0] 
def convert_variables(m): 
    name = m.group(1) 
    num_converted[0] += 1 
    return '<%%= %s %%>' % name 
+6

Bạn có thể giải thích tại sao điều đó là cần thiết không? Tôi đã dự kiến ​​sẽ OP mã để làm việc. –

+36

Bởi vì quy tắc phạm vi của Python bị mất trí nhớ. Sự hiện diện của toán tử gán '+ =' đánh dấu mục tiêu, 'num_converted', như là cục bộ cho phạm vi của hàm kèm theo, và không có cách âm trong Python 2.x để chỉ truy cập một mức độ phạm vi từ đó. Chỉ từ khóa 'global' mới có thể nâng các tham chiếu biến ra khỏi phạm vi hiện tại, và nó sẽ đưa bạn thẳng đến đỉnh. –

+0

Tôi đã sử dụng cách tiếp cận đó và nó hoạt động tốt. – ThiefMaster

9

Sử dụng từ khóa global là tốt. Nếu bạn viết:

num_converted = 0 
def convert_variables(m): 
    global num_converted 
    name = m.group(1) 
    num_converted += 1 
    return '<%%= %s %%>' % name 

... num_converted không trở thành một "biến toàn cầu" (nghĩa là nó không trở thành có thể nhìn thấy ở bất cứ nơi bất ngờ khác), nó chỉ có nghĩa là nó có thể thay đổi bên trong convert_variables. Điều đó có vẻ là chính xác những gì bạn muốn.

Để đặt theo cách khác, num_convertedđã là biến toàn cục. Tất cả các cú pháp global num_converted không là nói với Python "bên trong chức năng này, không tạo ra một num_converted biến địa phương, thay vào đó, sử dụng một trong những thế giới hiện

+3

'global' trong 2.x hoạt động khá nhiều cách' nonlocal' làm trong 3.x. –

+2

"Để đặt nó theo một cách khác, num_converted đã là một biến toàn cầu" - mã của tôi đang chạy bên trong một hàm .. do đó, nó hiện không phải là toàn cục. – ThiefMaster

+2

Ah, tôi đã không chú ý đến phần "bên trong một chức năng", xin lỗi - trong trường hợp đó, danh sách dài một danh sách của Marcelo có thể là một giải pháp tốt hơn (nhưng xấu xí). – Emile

6

Điều gì về việc sử dụng phiên bản lớp để giữ trạng thái? Bạn tạo một lớp và thông qua phương pháp dụ để tàu ngầm và những chức năng sẽ có một tài liệu tham khảo để tự ...

+7

Âm thanh hơi quá mức và giống như một giải pháp đến từ một lập trình viên Java. ; p – ThiefMaster

+1

hey, nhưng nó sạch sẽ! ;) – Seb

+1

@ThiefMaster Tại sao quá mức này? Nếu bạn muốn truy cập vào phạm vi cha mẹ, bạn nên ** sử dụng một lớp trong Python. – schlamar

25

(xem dưới đây để biết câu trả lời chỉnh sửa)

Bạn có thể sử dụng một cái gì đó như:

def convert_variables(m): 
    name = m.group(1) 
    convert_variables.num_converted += 1 
    return '<%%= %s %%>' % name 

convert_variables.num_converted = 0 

Bằng cách này, num_converted công trình như là một biến "tĩnh" C-như của phương pháp convert_variable


(chỉnh sửa)

def convert_variables(m): 
    name = m.group(1) 
    convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1 
    return '<%%= %s %%>' % name 

Bằng cách này, bạn không cần phải khởi tạo bộ đếm trong quy trình chính.

+3

Phải. Và lưu ý rằng bạn _must_ tạo thuộc tính 'convert_variables.num_converted' _after_ xác định hàm, mặc dù có vẻ lạ. –

+0

@PabloG câu trả lời thỏa mãn nhất cho câu hỏi này, ngoài nonlocal in 3.x; sử dụng loại có thể thay đổi [] là giải pháp thay thế rẻ. – user2290820

6

Tôi có vài nhận xét.

Đầu tiên, một ứng dụng cho các chức năng lồng nhau như vậy xuất hiện khi xử lý các cuộc gọi lại thô, như được sử dụng trong các thư viện như xml.parsers.expat. (Các tác giả thư viện đã chọn cách tiếp cận này có thể bị phản đối, nhưng ... có nhiều lý do để sử dụng nó.)

Thứ hai: trong một lớp, có nhiều lựa chọn thay thế đẹp hơn cho mảng (num_converted [0]). Tôi cho rằng đây là điều mà Sebastjan đang nói đến.

class MainClass: 
    _num_converted = 0 
    def outer_method(self): 
     def convert_variables(m): 
      name = m.group(1) 
      self._num_converted += 1 
      return '<%%= %s %%>' % name 

Vẫn còn đủ kỳ quặc để nhận xét trong mã ... Nhưng biến này ít nhất là cục bộ cho lớp học.

+0

Xin chào, chào mừng bạn đến với Stack Overflow - nhưng đăng một "nhận xét" như một câu trả lời không thực sự là điều bạn có thể làm ở đây. Chúng tôi có ý kiến ​​cho điều này (tuy nhiên, bạn cần một số đại diện để đăng chúng - nhưng không đăng câu trả lời chỉ vì bạn không có đủ đại diện cho nhận xét) – ThiefMaster

+5

Xin chào, Bạn cũng được chào đón! Tôi không hiểu nhận xét của bạn hoặc một số thuật ngữ bạn sử dụng. Và tôi là một người đàn ông bận rộn, chỉ cố gắng giúp đỡ! –

+0

Mặc dù vậy, hãy xem http://stackoverflow.com/about. Trong khi được hữu ích luôn luôn đánh giá cao một bình luận được đăng cuối cùng sẽ bị xóa không có vấn đề như thế nào tốt nó được. – ThiefMaster

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