# python3小白课:局部函数 ### 局部函数定义 前面我们学习的函数都是在全局范围内定义的,在整个py脚本中都可以调用到它。如果在函数定义的内部再定义函数呢?python也是支持的,这时候的函数就称之为`局部函数`。 接下来我们把在全局范围定义的函数称为封闭函数,封闭函数内定义的函数称之为局部函数。默认情况下,局部函数对外是隐藏的,仅能在封闭函数内进行调用。但你也可以做出一些变动,让外部也能调用到局部函数来进行计算。 我们来看个小例子: ```python # coding:utf-8 def fengbi(method, num): def add_1(num): return num + 1 def add_2(num): return num + 2 def add_3(num): return num + 3 if method == "add_1": return add_1(num) elif method == "add_2": return add_2(num) elif method == "add_3": return add_3(num) else: return "不支持的方法名" print(fengbi("add_1", 1)) print(fengbi("add_2", 1)) print(fengbi("add_4", 1)) ``` 由于直接在内部定义并调用比较简单我们就不举例子了。我们来举一个从外部调用到内部局部函数的方法例子,这里我们定义了一个封闭函数,支持传入方法名,在函数内部首先定义好那几个局部函数方法,后续可以通过if结构判断给的是什么方法,来调用具体的局部函数返回。 --- ### 局部函数访问封闭函数中的局部变量 > [!note] > > 本部分知识比较不常用,可以学习也可以跳过。 由于我们这里在函数内部又有函数定义,势必有可能会遇到在局部函数中调用到封闭函数中定义的局部变量的情形。此时还是跟之前学习作用域时讲的一样,可以在局部函数中直接调用在封闭函数中定义的局部函数。(这么听起来比较拗口,我们看个例子吧) ```python # coding:utf-8 def fengbi(): num = 1 def add_1(): return num + 1 print(add_1()) fengbi() ``` 可见,是可以直接访问的。如果我们在局部函数中修改封闭函数中的局部变量呢? ```python # coding:utf-8 def fengbi(): num = 1 def add_1(): print(num) num = 2 return num + 1 print(add_1()) fengbi() ``` 这次运行又报错了,报错信息为:`UnboundLocalError: local variable 'num' referenced before assignment`,看起来还是我们讲变量作用域时说过的问题。怎么解决呢?此时我们就不用`global`语句了,因为那是对付全局变量的。我们可以使用`nonlocal`语句来解决,用法跟`global`语句是一致的。 ```python # coding:utf-8 def fengbi(): num = 1 def add_1(): nonlocal num print(num) num = 2 return num + 1 print(add_1()) print(num) fengbi() ``` 此时我们会发现,num在上述代码中就一直指代封闭函数中的`num`局部变量了,不会有报错的情况发生,对于`num`的修改也会造成`num`局部变量的值改变。 如果你对这部分知识感兴趣,可以往下学习我们下面的`函数:前沿`部分。 --- ### 函数:前沿 关于函数的知识,我们还有一些高级内容,但作为入门小白,经过审慎考虑,我认为像市面上一本百科全书一样把一个东西讲得太多太难太细对大家是没有太大帮助的,大家只会学着学着感叹,哇呀,怎么函数这么难?这么多知识这么难记?哇呀,python太难学了我不学了。 为了避免大家出现这种难过的情绪,我希望大家始终保持必要的学习激情,我们前面讲过的知识,基础运用是非常简单的,如果你对于某一块有兴趣,可以找找网上的资料,进一步自学即可,在入门时不必太过于执着和纠结你听不懂记不住的知识,够用就好。 > [!note] > > 关于函数的一些更难的知识,我们将会安排在进阶或高级课程中跟大家一起去啃这些硬骨头,它们适合python编程已经有一定基础,想要进一步突破瓶颈的同学。 这里我们就着前面讲过的内容,再扩展一点点前沿知识,如果你学不会看不懂,请跳过,没有关系的,当你做好了进一步突破瓶颈的准备,比如在一个美好的清晨,你休息得很好,脑袋适合研究点什么,再回过头来听一听你感兴趣的内容。 \*\*\*华丽的分割线\*\*\* 好,给大家随便灌了点鸡汤,我们继续讲课。 函数还有比较高级的定义方法,因为函数本身是一个对象,所以函数本身既可以用于赋值,也可以用于作为调用的参数传进别的函数中,还可以作为其他函数的返回值。 这里我们来讲个小例子,看下函数作为返回值是怎样的。 ```python # coding:utf-8 def createCounter(): n = 0 def counter(): nonlocal n n += 1 return n return counter # 验证计数器 mycounter = createCounter() for i in range(5): print(mycounter()) ``` 这是从网上找到的一个小题目,代码也是参考了下网上的朋友的,做了一些优化修改。 我们的题目是定义一个函数计数器,这个计数器内部可以存档一个计数,每次调用函数的时候,可以实现计数次数的增长。 首先我们可以先跑一下运行结果,可以看到每次输出的结果都会自增1,分别输出`1, 2, 3, 4, 5`这几个值来。 然后我们来分析一下代码。 首先我们定义了一个`createCounter`创建计数器的函数,这个函数内部呢,定义了一个`n`变量,这个变量就是计数器的值了。接着我们定义了一个`counter`的局部函数,这个函数就使用了我们前面讲到的`nonlocal`语句,申明局部函数用到的`n`变量是用的封闭函数中的那个,这样后续赋值就不会重新定义一个`n`变量了,实现了访问变量的目的。接下来在局部函数内将变量的值自增1,并返回最新的结果。 需要注意的是,这个`createCounter`函数的return语句返回的是一个函数,如果返回函数`counter`,直接写函数名即可,没有括号什么的。 这样返回如何调用呢?首先运行这个`createCounter`函数,将它的返回值赋值给一个变量`mycounter`,此时`mycounter`就有了`counter`函数的功能了。接下来在调用时,只需要调用`mycounter`这个函数即可。 > [!note] > > 这是一种闭包的概念,如果我们的n是一个全局变量,那么这个计数器在每次使用时,你还需要在自己的代码中定义它,或者需要查看这个函数内部的代码实现。 > > 当使用闭包后,变量n的作用域仅限于封闭函数中,这在某些时候就给了使用者一些便利。 在python脚本运行期间,每次调用,我们会发现`n`的值,是在不断自增的,你可以理解为,我们只访问了`counter`函数,并没有再次访问`createCounter`函数,所以这个`n`不会重新赋值,而`counter`函数又能访问到`n`并自增,这样就实现了一个计数器的效果。 是不是很有意思呢? > [!note] > > 关于函数还有其他知识点,我们在入门课就不跟大家讲那么深那么难了,感兴趣的同学可以关注我们的进阶或高级课程学习或者自行网上查阅资料学习。 ### 单词释义 | 单词 | 释义 | | ------------- | ------------------------------------------------------------ | | method | 方法 | | nonlocal | non为否定前缀,local为局部的,在这里理解为非局部的 | | createCounter | create创建,Counter计数器,count计数,合起来理解为创建计数器 | | counter | 计数器 | | mycounter | my我的,理解为我的计数器 |