在Python 2中,如何在父作用域中写入变量?

时间:2011-01-31 13:37:56

标签: python closures scope python-2.x

我在函数中有以下代码:

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:赋值前引用的局部变量'num_converted'

我可以使用global,但全局变量很难看,我真的根本不需要该变量。

所以我很好奇如何写入父函数范围内的变量。 nonlocal num_converted可能会完成这项工作,但我需要一个适用于Python 2.x的解决方案。

6 个答案:

答案 0 :(得分:85)

问题:这是因为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

答案 1 :(得分:29)

(见下面的编辑答案)

您可以使用以下内容:

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

这样,您无需在主过程中初始化计数器。

答案 2 :(得分: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语法都告诉Python“在这个函数内部,不要创建一个本地num_converted变量,而是使用现有的全局变量。

答案 3 :(得分:7)

使用类实例来保存状态怎么样? 您实例化一个类并将实例方法传递给subs,这些函数将引用self ...

答案 4 :(得分:6)

我有几点评论。

首先,在处理原始回调时会出现一个这样的嵌套函数的应用程序,就像xml.parsers.expat这样的库中使用的那样。 (图书馆作者选择这种方法可能是令人反感的,但是......仍然有理由使用它。)

第二:在一个类中,有更好的替代数组(num_converted [0])。我想这就是Sebastjan所说的。

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

在代码中值得评论仍然很奇怪...... 但是变量至少是本地的本地变量。

答案 5 :(得分:0)

修改自:https://stackoverflow.com/a/40690954/819544

您可以利用inspect模块访问调用范围的全局字典并写入。这意味着甚至可以利用这个技巧从导入的子模块中定义的嵌套函数访问调用范围。

import inspect 

def get_globals(scope_level=0):
    return dict(inspect.getmembers(inspect.stack()[scope_level][0]))["f_globals"]

num_converted = 0
def foobar():
    get_globals(0)['num_converted'] += 1

foobar()
print(num_converted) 
# 1

根据需要使用scope_level参数。设置scope_level=1适用于子模块中定义的函数,scope_level=2适用于子模块中装饰器中定义的内部函数等。

注意:仅仅因为你可以这样做,并不意味着你应该这样做。