在 Python2中,如何写入父作用域中的变量?

我有这样的代码:

def example():
# other logic omitted


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)


# more logic...

stored_blocks添加元素可以很好地工作,但是我不能在第二个嵌套函数中增加 num_converted。我得到一个异常,说 UnboundLocalError: local variable 'num_converted' referenced before assignment

我知道在3.x 中可以尝试 nonlocal num_converted,但是如何在2.x 中解决这个问题呢?我不想使用全局变量。

26518 次浏览

使用 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内部被修改。这似乎正是你想要的。

To put it another way, num_converted is 已经 a global variable. All the global num_converted syntax does is tell Python "inside this function, don't create a local num_converted variable, instead, use the existing global one.

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

使用类实例来保存状态如何? 您实例化一个类,并将实例方法传递给 subs,这些函数将有一个对 self... 的引用。

(参见下面的编辑答案)

你可以这样写:

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

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

我有几句话要说。

首先,在处理原始回调时会出现这种嵌套函数的一个应用程序,就像在 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

这还是很奇怪,值得在代码中注释..。 但是该变量至少是该类的本地变量。

改编自: 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,等等。

NB: Just because you 可以 do this, doesn't mean you should.