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

发布于 2024-10-15 00:38:00 字数 853 浏览 3 评论 0原文

我有一些代码,例如:

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

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

I have some code like:

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...

Adding elements to stored_blocks works fine, but I cannot increase num_converted in the second nested function. I get an exception that says UnboundLocalError: local variable 'num_converted' referenced before assignment.

I know that in 3.x, I could try nonlocal num_converted, but how can I solve the problem in 2.x? I don't want to use a global variable for this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

云柯 2024-10-22 00:38:00

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

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 global keyword can lift variable references out of the current scope, and it takes you straight to the top.

Fix: Turn num_converted into a single-element array.

num_converted = [0]
def convert_variables(m):
    name = m.group(1)
    num_converted[0] += 1
    return '<%%= %s %%>' % name
童话里做英雄 2024-10-22 00:38:00

(请参阅下面的编辑答案)

您可以使用类似的东西:

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

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

(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_converted works as a C-like "static" variable of the convert_variable method


(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.

戏舞 2024-10-22 00:38:00

使用 global 关键字就可以了。如果你写:

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

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

换句话说,num_converted已经是一个全局变量。 global num_converted 语法所做的一切就是告诉 Python“在这个函数内,不要创建本地 num_converted 变量,而是使用现有的全局变量。

Using the global keyword is fine. If you write:

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

... num_converted doesn'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.

To put it another way, num_converted is already 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.

热情消退 2024-10-22 00:38:00

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

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...

不必在意 2024-10-22 00:38:00

我有几句话。

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

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

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.)

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

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.

涙—继续流 2024-10-22 00:38:00

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

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

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 适用于子模块中的装饰器中定义的内部函数,等等。

注意:只是因为您 < em>可以这样做,并不意味着你应该这样做。

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

You can leverage the inspect module 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.

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_level argument as needed. Setting scope_level=1 works for a function defined in a submodule, scope_level=2 for the inner function defined in a decorator in a submodule, etc.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文