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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4851463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 17:50:28  来源:igfitidea点击:

In Python 2, how do I write to variable in the parent scope?

pythonclosuresscopepython-2.x

提问by ThiefMaster

I have the following code inside a function:

我在函数中有以下代码:

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)

Adding elements to stored_blocksworks fine, but I cannot increase num_convertedin the second subfunction:

添加元素stored_blocks工作正常,但我不能num_converted在第二个子函数中增加:

UnboundLocalError: local variable 'num_converted' referenced before assignment

UnboundLocalError:赋值前引用了局部变量“num_converted”

I could use globalbut global variables are ugly and I really don't need that variable to be global at all.

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

So I'm curious how I can write to a variable in the parent function's scope. nonlocal num_convertedwould probably do the job, but I need a solution that works with Python 2.x.

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

采纳答案by Marcelo Cantos

Problem:This is because Python's scoping rules are demented. The presence of the +=assignment operator marks the target, num_converted, as local to the enclosing function's scope, and there is no sound way in Python 2.x to access just one scoping level out from there. Only the globalkeyword can lift variable references out of the current scope, and it takes you straight to the top.

问题:这是因为 Python 的范围规则是疯狂的。+=赋值运算符的存在将目标 , 标记num_converted为封闭函数作用域的局部对象,并且在 Python 2.x 中没有健全的方法来仅从那里访问一个作用域级别。只有global关键字可以将变量引用提升到当前范围之外,它会将您直接带到顶部。

Fix:Turn num_convertedinto a single-element array.

修正:num_converted成一个单一的元素数组。

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

回答by Emile

Using the globalkeyword is fine. If you write:

使用global关键字没问题。如果你写:

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

... num_converteddoesn't become a "global variable" (i.e. it doesn't become visible in any other unexpected places), it just means it can be modified inside convert_variables. That seems to be exactly what you want.

...num_converted不会成为“全局变量”(即它不会在任何其他意想不到的地方变得可见),它只是意味着它可以在convert_variables. 这似乎正是你想要的。

To put it another way, num_convertedis alreadya global variable. All the global num_convertedsyntax does is tell Python "inside this function, don't create a local num_convertedvariable, instead, use the existing global one.

为了把它的另一种方式,num_converted已经一个全局变量。所有的global num_converted语法都是告诉 Python “在这个函数中,不要创建局部num_converted变量,而是使用现有的全局变量。

回答by Seb

What about using a class instance to hold the state? You instantiate a class and pass instance methods to subs and those functions would have a reference to self...

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

回答by PabloG

(see below for the edited answer)

(有关编辑后的答案,请参见下文)

You can use something like:

您可以使用以下内容:

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

convert_variables.num_converted = 0

This way, num_convertedworks as a C-like "static" variable of the convert_variable method

这样,num_converted作为 convert_variable 方法的类似 C 的“静态”变量工作



(edited)

(已编辑)

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

This way, you don't need to initialize the counter in the main procedure.

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

回答by Steve White

I have couple of remarks.

我有几点意见。

First, one application for such nested functions comes up when dealing with raw callbacks, as are used in libraries like xml.parsers.expat. (That the library authors chose this approach may be objectionable, but ... there are reasons to use it nonetheless.)

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

Second: within a class, there are much nicer alternatives to the array (num_converted[0]). I suppose this is what Sebastjan was talking about.

第二:在一个类中,有更好的替代数组 (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

It's still odd enough to merit a comment in the code... But the variable is at least local to the class.

值得在代码中添加注释仍然很奇怪……但该变量至少是类的局部变量。

回答by David Marx

Modified from: https://stackoverflow.com/a/40690954/819544

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

You can leverage the inspectmodule to access the calling scope's globals dict and write into that. That means this trick can even be leveraged to access the calling scope from a nested function defined in an imported submodule.

您可以利用该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

Play with the scope_levelargument as needed. Setting scope_level=1works for a function defined in a submodule, scope_level=2for the inner function defined in a decorator in a submodule, etc.

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

NB: Just because you cando this, doesn't mean you should.

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