Python:嵌套函数和变量范围

时间:2014-10-16 16:02:28

标签: python nested

此代码无效:

def lol():
    i = 1
    def _lol():
        i += 1
    _lol()
lol()

错误:

local variable 'i' referenced before assignment

但是,以下代码可以正常工作:

def lol():
    i = [1]
    def _lol():
        i[0] += 1
    _lol()
lol()

为什么?

1 个答案:

答案 0 :(得分:4)

Python范围分为3个类别 - localnonlocalglobal。默认情况下,函数只能更改本地范围中的引用(使用赋值运算符创建引用)。

你可以 mutate 一个你引用的对象,这就是为什么第二个例子有效的原因(i是对列表[1]的引用,然后你改变/改变它的第一项)。简而言之,您变异 i引用的对象,您不是要更改引用。请注意,您可以通过global关键字提供函数访问权限以更改全局范围中的引用:

i = 1
def func():
  global i  # If you comment this out, i doesn't get changed in the global scope
  i = 2

func()
print(i)  # 2 -- 1 if the global statement is commented out.

请注意,python3.x添加了nonlocal关键字。它与global完全相同,但与非本地范围相同。 e.g。

def foo():
    i = 1  # nonlocal to bar
    def bar():
        nonlocal i 
        print(i)
        i += 1
    return bar

bar1 = foo()
bar1()  # 1
bar1()  # 2
bar1()  # 3

bar2 = foo()
bar2()  # 1
bar2()  # 2

bar1()  # 4  bar2 doesn't influence bar1 at all.

增强运算符

这有点高级,但希望有助于回答有关+=等运营商的问题。考虑一下这个案例:

x = []
def func():
    x += [1]

您可能希望这样做 - 毕竟,列表x += [1]的{​​{1}}实际上只是x对吗?。不幸的是,它并不完全。我们可以使用x.extend([1])反汇编func以查看更多内容。

dis.dis

注意字节码指令STORE_FAST?基本上说,将INPLACE_ADD的结果存储在本地字典中的名称>>> dis.dis(func) 2 0 LOAD_FAST 0 (x) 3 LOAD_CONST 1 (1) 6 BUILD_LIST 1 9 INPLACE_ADD 10 STORE_FAST 0 (x) ### IMPORTANT! 13 LOAD_CONST 0 (None) 16 RETURN_VALUE 中。换句话说,你写:

x

但是python执行 1

x += [1]

为什么呢? x = x.__iadd__([1]) 应该运行到位,为什么需要将名称重新绑定到__iadd__的返回值?重新绑定部分是问题 - 即,此代码工作:

__iadd__

答案是因为python具有不可变对象,x = [] def func(): x.__iadd__([1]) 也需要使用它们。因此,__iadd__可以返回“__iadd__”以外的对象。这最终会非常有用。考虑self。此调用仅起作用,因为允许i = 1; i += 1返回新的整数。

1 更深入地讨论这个问题实际上是我在StackOverflow上最受欢迎的答案,可以找到here