作者回复: 这个问题非常好!首先我们先从nonlocal说起,python在使用变量的时候要遵循一个LEGB规则, 什么又是LEGB呢,就是如果你使用了一个函数内的变量做运算,python会从函数里面找这个变量的定义,如果找不到函数里面的定义就会报错了--即你看到的未定义先使用的错误了。 这种实现方式比js要好,避免在你忘记声明局部变量的时候,误使用了全局变量。 那变量都有几个作用域(影响范围)呢?一共是四个,分别是局部(local)-闭包(enclosing)-全局(global)-模块(builtin),LEGB就是取的他们的首字母; 如果要引用的变量的定义没在内部函数里面,而是在闭包里面就可以通过nonlocal声明一下,python就会从外层函数里面找这个变量的定义了,如果使用了global关键字修饰内部函数的变量,运算时就会从全局变量里面找变量的定义了。 上面说的是变量作用域,再来说下我为什么使用列表,这里使用列表的目的就是为了达到 nonlocal变量的功能,因为对列表的操作是直接操作内存的位置,对变量的操作是重新分配了一块新的内存; 所以在内部函数直接使用列表的名称也是操作外部函数定义的列表。这里需要通过python底层执行过程观察: from dis import dis # dis模块可以反汇编python函数的字节码 def counter2(first2=0): CNT2 = first2 def add_one(): # nonlocal CNT2 CNT2 += 1 return CNT2 return add_one dis(counter2(3)) 如果将nonlocal注释掉会显示反汇编结果为 LOAD_FAST 0 (CNT2) 如果不注释nonlocal或使用列表会显示反汇编结果为 LOAD_DEREF 0 (CNT2) LOAD_FAST 的含义是加载了本地变量 LOAD_DEREF 的含义是加载了引用的变量 LOAD_GLOBAL 的含义是加载了全局变量 这里涉及到python作用域和底层的一些原理,希望你能通过尝试更多的示例进行学习总结
作者回复: 很细心,第一个问题我在解释的时候不严谨,如果函数内只引用全局变量,但不修改时可以不使用global关键字,一旦对全局变量进行修改必须声明,如:在函数内使用cnt2 += 1 就会报错了,这种情况下强制使用 global cnt2 显式声明才可以正常使用 def add_one(): global cnt2 # 声明 cnt2 += 1 # 对全局变量做修改 a = cnt2 + 1 print(a) return a 第二个问题的理解是对的,能够正确理解变量作用域对工作中有非常大的帮助,继续加油!
作者回复: 这里涉及函数执行的顺序问题,使用FIRST = 0作为参数,在定义函数时FIRST就得到了 0, 如果没有传值,那么FIRST就取得0了, 如果给函数传值,FIRST就会取得传递的值,覆盖掉0,FIRST就是新的值了
作者回复: 对的,因为面对初学者,我没有太理论化的解释作用域的问题。更多关于作用域的解释可以参考搜索引擎和我的其他留言回复。
作者回复: 在函数中定义另一个函数称为嵌套函数,嵌套函数可以访问包围范围内的变量,所以不需要global声明
作者回复: https://docs.python.org/zh-cn/3.7/library/functions.html 内置函数的官方文档我提供一下,主要给读者提供一个方法,逐一讲解就太啰嗦了
作者回复: 因为视频里出现了多次传值,我不太清楚你的问题是指哪个代码,需要问题再详细一点,如果还是没搞明白传值的过程,可以补充一下问题,或者按照你的理解描述一遍问题,我来详细回答你
作者回复: TypeError: counter() takes 0 positional arguments but 1 was given 注意这里,是不应该为counter带参数的,因为你定义这个函数的时候就没有指定变量来接收参数
作者回复: 因为声明的global a,b 就是全局变量初始化的 a = 0 b = 10 而且这里和x,y 没有必然联系,所以没有从2和20开始计数
作者回复: 要看 cnt[0] 是什么类型