Python:嵌套函数和变量范围

标签 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()

这是为什么?

最佳答案

Python 作用域分为 3 类——localnonlocalglobal。默认情况下,函数只能更改局部范围内的引用(引用是使用赋值运算符创建的)。

您可以自由改变您引用的对象,这就是第二个示例起作用的原因(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 做同样的事情,但对非本地范围。例如

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] 对于列表 x 实际上只是 x.extend([1 ])对吧?。不幸的是,事实并非如此。我们可以使用 dis.dis 反汇编 func 以进一步了解发生了什么。

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

注意字节码指令STORE_FAST ?这基本上就是说,存储 INPLACE_ADD 的结果在本地字典中的名称 x 中。换句话说,你写:

x += [1]

但是 python 执行1:

x = x.__iadd__([1])

为什么? __iadd__ 应该就地运行,那么为什么它需要将名称重新绑定(bind)到 __iadd__ 的返回值呢?重新绑定(bind)部分是问题所在——也就是说,这段代码工作:

x = []
def func():
    x.__iadd__([1])

答案是因为 python 具有不可变对象(immutable对象),__iadd__ 也需要与它们一起工作。因此,__iadd__ 可以返回“self”以外的对象。这最终非常有用。考虑 i = 1;我 += 1。此调用之所以有效,是因为 int.__iadd__ 被允许返回一个新的整数。

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

关于Python:嵌套函数和变量范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26408941/

相关文章:

c++ - 读取三重嵌套 for 循环

json - 使用 jq 将结构化 JSON 转为 CSV

python - numpy trapz 行为在 x 值处具有双重定义的 y 值(冲突点)

c# - 在 C# 中运行带有导入的 Python 脚本

python - MongoEngine删除文件

python - 为 16 位深度图像着色的正确方法是什么?

javascript - 如何从 javascript 中的嵌套异步函数传播返回值

ruby - 反射(reflection)嵌套命名空间

MySQL 选择里面的选择里面的选择

python - 让需求直接指向 git 存储库而不是 PyPi 有什么好处?