2011-01-31 67 views
68

我有一個函數內部下面的代碼:Python的封:寫入變量父範圍

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) 

將元素添加到stored_blocks工作正常,但在第二子功能我不能增加num_converted

UnboundLocalError: local variable 'num_converted' referenced before assignment

我可以使用global但全局變量是醜陋的,我真的不需要該變量是全球性的。

所以我很好奇我如何寫入父函數範圍內的變量。 nonlocal num_converted可能會完成這項工作,但我需要一個適用於Python 2.x的解決方案。

+4

與某些流行的觀點相反(通過這類問題來判斷)`def`並不是唯一定義名稱空間的關鍵字:也有'class`。 – 2011-01-31 14:22:41

回答

71

問題:。這是因爲Python的作用域規則癡呆症的存在+=賦值運算符將目標num_converted標記爲封閉函數作用域的本地對象,並且在Python 2.x中沒有任何聲音方式可以從中訪問一個作用域級別,只有global關鍵字可以將變量引用從目前的範圍,它需要你直接到頂端

修復:num_converted轉換爲單元素數組。

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

你能解釋爲什麼這是必要的嗎?我會希望OP代碼工作。 – 2011-01-31 13:47:49

+36

因爲Python的範圍規則是瘋狂的。 `+ =`賦值運算符的存在將目標文件`num_converted`標記爲封閉函數作用域的本地對象,並且在Python 2.x中沒有任何聲音方式可以從那裏訪問一個範圍級別。只有`global`關鍵字可以將可變引用從當前作用域提取出來,並且會直接進入頂端。 – 2011-01-31 13:51:13

9

使用global關鍵字很好。如果你寫:

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

... num_converted不會成爲「全局變量」(即,它不以任何其他意想不到的地方變得可見),它只是意味着它可以在裏面convert_variables進行修改。這似乎正是你想要的。

換句話說,num_converted已經是一個全局變量。所有global num_converted語法做只是告訴蟒「這個功能裏面,不創建一個本地num_converted變量,而是使用現有的全球一個

+3

2.x中的`global`幾乎在3.x中如何處理`nonlocal`。 – 2011-01-31 13:49:09

+2

「換句話說,num_converted已經是一個全局變量」 - 我的代碼在一個函數內運行..所以它目前不是全局的。 – ThiefMaster 2011-01-31 13:53:57

+2

啊,我並沒有注意到「功能內部」部分,對不起 - 在這種情況下,馬塞洛的長度單列表可能是一個更好的(但醜陋的)解決方案。 – Emile 2011-01-31 13:55:50

6

如何使用類實例來保存狀態? 你實例化一個類,並通過實例方法,以潛艇和這些功能必須自我參考...

25

(見下面的編輯答案)

您可以使用類似:

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

convert_variables.num_converted = 0 

這樣,num_converted作品作爲convert_variable方法的類C的 「靜態」 可變


(編輯)

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

這樣,您不需要在主過程中初始化計數器。

6

我有幾句話。

首先,處理原始回調函數時會出現這種嵌套函數的一個應用程序,就像xml.parsers.expat這樣的庫中使用的那樣。 (圖書館作者選擇這種方法可能是令人反感的,但是......仍有理由使用它。)

第二:在一個類中,有更好的替代數組(num_converted [0])。我想這就是塞巴斯蒂安正在談論的。

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

它仍然不夠多,值得在代碼... 註釋,但該變量至少是本地的類。