2011-01-31 172 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 

它仍然不够多,值得在代码... 注释,但该变量至少是本地的类。