Python闭包:写入父作用域中的变量

Thi*_*ter 77 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)
Run Code Online (Sandbox Code Playgroud)

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

UnboundLocalError:赋值前引用的局部变量'num_converted'

我可以使用,global但全局变量是丑陋的,我真的不需要该变量是全局的.

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

Mar*_*tos 86

问题:这是因为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
Run Code Online (Sandbox Code Playgroud)

  • 因为Python的范围规则是疯狂的.`+ =`赋值运算符的存在将目标`num_converted`标记为封闭函数范围的本地,并且Python 2.x中没有声音方法可以从那里访问一个范围级别.只有`global`关键字可以将变量引用提升到当前范围之外,它会直接将您带到顶部. (36认同)
  • @schlamar:所讨论的变量在任何意义上都不是全局的.OP的开头句规定他们提供的整个代码块都在一个函数内. (15认同)
  • 你能解释为什么那是必要的吗?我本以期望OP代码能够运行. (7认同)
  • 这并不聪明,这实际上是非常糟糕的代码.有课程(见问题评论).此版本使用您应该始终避免的全局变量.不使用`global`并不意味着你没有全局变量. (6认同)
  • @schlamar:一个变量需要很多脚手架。如果您需要的答案比我的答案提供的要多(真的吗?),我建议`scope = {'num_converted':0}…scope ['num_converted'] + = 1`。 (2认同)

Pab*_*loG 29

(见下面的编辑答案)

您可以使用以下内容:

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

convert_variables.num_converted = 0
Run Code Online (Sandbox Code Playgroud)

这样,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
Run Code Online (Sandbox Code Playgroud)

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

  • 对.请注意,您_must_创建定义函数的属性`convert_variables.num_converted` _after_,尽管看起来很奇怪. (3认同)

Emi*_*ile 9

使用global关键字很好.如果你写:

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

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

换句话说,num_converted已经是一个全局变量.所有global num_converted语法都告诉Python"在这个函数内部,不要创建局部num_converted变量,而是使用现有的全局变量.

  • 2.x中的`global`几乎与3.x中的`nonlocal`有关. (4认同)
  • "换句话说,num_converted已经是一个全局变量" - 我的代码在函数内部运行..所以它目前不是全局的. (2认同)
  • 啊,我没有注意到"内部功能"部分,对不起 - 在这种情况下,Marcelo的长度列表可能是一个更好(但丑陋)的解决方案. (2认同)

Seb*_*Seb 7

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

  • 听起来有点过于苛刻,就像来自Java程序员的解决方案.,p (7认同)
  • @schlamar因为在其他所有语言中都有一流的函数支持(JS,函数式语言)闭包只是工作. (2认同)

Ste*_*ite 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
Run Code Online (Sandbox Code Playgroud)

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

  • 嗨,也欢迎你!我不明白你的评论或你使用的几个术语.我是一个忙碌的人,只是想要有所帮助! (5认同)