Python yield 生成器变量作用域

4
我使用yield创建一个生成器,它返回通过正则表达式和re.sub()提取的字符串块。虽然我找到了一种可行的方法,但我有点困惑为什么它可以这样工作而另一种方式不行,如下所示:
这个方法行不通(processchunk()没有将结果分配给在splitmsg中声明的chunk):
def splitmsg(msg):
    chunk = None
    def processchunk(match):
        chunk = match.group(1)
        return ""
    while True:
        chunk = None
        msg = re.sub(reCHUNK,processchunk,msg,1)
        if chunk:
            yield chunk
        else:
            break     

这是有效的(请注意唯一的区别是chunk现在是一个块列表):
def splitmsg(msg):
    chunks = [ None, ]
    def processchunk(match):
        chunks[0] = match.group(1)
        return ""
    while True:
        chunks[0] = None
        msg = re.sub(reCHUNK,processchunk,msg,1)
        if chunks[0]:
            yield chunks[0]
        else:
            break

我的问题基本上是为什么似乎块/块变量的作用域取决于它是普通变量还是列表?

可能是Python变量作用域问题的重复。 - user395760
2个回答

5
在Python中,如果读取变量,则可以从周围的范围中“提取”变量。因此以下代码可以正常工作:
def foo():
    spam = 'eggs'
    def bar():
        print spam
foo()

由于变量'spam'是在周围的作用域中查找的,因此foo函数会这样做。
然而,您无法更改周围作用域的值。您可以更改全局变量(如果在函数中声明它们为global),但您不能对上述函数中的变量spam这样做。
(Python 3更改了这一点,它添加了一个新关键字nonlocal。如果您将spam定义为bar内部的nonlocal,则可以在bar内部为该变量分配新值。)。
现在来看您的列表。发生的情况是您根本没有更改变量chunks。在整个代码中,chunks只指向一个列表,并且仅指向该列表。就Python而言,在processchunk函数中未更改变量chunks
发生的事情是您更改了列表的内容。您可以自由地为chunks[0]分配一个新值,因为那不是变量chunks,而是由chunks引用的列表,第一个索引。 Python之所以允许这样做,是因为它不是变量分配,而是列表操作。
因此,您的“解决方法”是正确的,但有些晦涩。如果您使用Python 3,则可以在processchunk中将chunks声明为nonlocal,然后即可在没有列表的情况下工作。

@aaa90210:chunk = match.group(1) 遮蔽了 非本地变量 chunk - Steven Rumbalski
@aaa90210: chunk[0] = match.group(1)chunk.__setitem__(0, match.group(1)) 的语法糖。尽管看起来像是赋值,但实际上并不是。 - Steven Rumbalski

1
在第一种情况下,您正在创建一个名为chunk的新本地变量。如果您在函数内部对其进行赋值,则将变量视为局部变量。在第二种情况下,您正在修改外部变量chunk所引用的列表。因为您没有对此变量进行赋值,所以它不被视为局部变量。请参见this previous question
在Python中,对裸名称进行赋值(someName = ...)与其他任何操作都不同;特别是它与项赋值(someName[0] = ...)不同。后者在幕后调用方法来改变列表。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接