(十七)函数的返回值与嵌套函数
1:什么是返回值
返回值指的是函数返回的结果 # 示例1 def add(x, y): print(x, y) return x + y # 返回值, return以后的语句将不会再执行 print(x) print(y) add(1,2) # 示例2 def add(x, y): print(x, y) return x + y # 函数体内,碰到return语句,函数执行完毕,之后的语句将不会再执行 return x - y # 不会再次执行 add(1,2)
2:函数的隐式返回和显示返回
Python函数都有返回值,如果函数体内没有return语句,那么默认返回None,称为隐式返回
如果有return语句,直接返回,称为显示返回
3:函数多值返回
函数可以返回多个值 def get_data(x,y,z): return x + 1, y + 1, z + 1 print(get_data(1,2,3)) # 得到的是一个元组 def get_data(x,y,z): return [x + 1, y + 1, z + 1] print(get_data(1,2,3)) # 得到的是一个列表
4:什么是嵌套函数
def outer_function(): # 内部函数 def inner_function(): print("inner_function") print("outer_function") # 内部调用 inner_function() outer_function() 注意:inner_function函数只能在outer_function函数内部调用,无法再outer_function函数外部被调用
5:函数作用域
什么是作用域:作用域指的是,一个标识符的作用范围就是这个标识符的作用域 # 示例1: z变量只能在函数内部被访问无法在函数外部被访问,z变量的作用域就是add函数内部 def add(x, y): z = 100 print(x, y , z) add(1,2) print(z) # 示例2:z可以在add函数外部被访问到, z = 100 def add(x, y): print(x, y , z) add(1,2) print(z) 小结: 全局作用域:整个运行环境中都可见,也就是整个模块内可见 局部作用域:函数或类中可见,函数体或类就是作用域
6:函数的闭包
# 对比下列中的函数 # 示例1 x=5 def add(): y = x + 1 add() # 示例2 x=5 def add(): x = x + 1 # 报错:local variable 'x' referenced before assignment add() 报错原因分析: 在示例1中,函数内部引用的x为函数外部的x, 因此x的值为5 在示例2中,函数内部重新定义了x的值,那么整个函数内部都会使用这个内部x,因此在运算x + 1的时候,x还没有完成定义就被引用了,这里的x引用的一定是内部正在定义的x,不是函数外部的x=5 # 示例3: x=5 def add(): print(x) # 这里的x引用的是x = 1的x,但是执行print(x)时,x还没有被定义,所以报错:local variable 'x' referenced before assignment x = 1 add() # 4 闭包: 内部函数引用了外部函数的变量,这就是闭包的定义 def outer_function(): x = 100 def inner_function(): print(x) # 内部函数引用了外部函数的自由变量 return inner_function ret = outer_function() ret()
7:nonlocal关键字、global关键字
# 1:global关键字 x=5 def add(): global x # 使用global关键字,指定x的引用全局的x变量 x = x + 1 add() # global关键字对全局变量的污染,因此需要慎用 x = 100 def foo(): global x # x被声明了全局变量 x = 10 x += 1 print(x) # x的值为11 foo() print(x) # x的值为11, 因此global关键字污染了全局变量x # 2:nonlocal关键字 def outer_function(): x = 100 def inner_function(): x = x + 1 # 这样会报错 print(x) return inner_function ret = outer_function() ret() # 修改上面的代码,使用nonlocal关键字 def outer_function(): x = 100 def inner_function(): nonlocal x x = x + 1 # 这样会报错 print(x) return inner_function ret = outer_function() ret() 注意:nonlocal关键字的意义在于:内部函数的变量引用的外部函数变量的值,不是全局作用域的值,因此不会污染全局作用域
8:函数默认值的作用域
# 示例, 函数的默认值绑定在函数对象的整个生命周期,不会因为函数内部对默认值操作而发生改变 def add(lst = []): lst.append('hello') print(lst) add() # 输出:['hello'] print('id={}'.format(id(add))) # 函数对象的id值不变,调用的是同一个函数 add() # 输出['hello', 'hello'] print('id={}'.format(id(add))) # 函数对象的id值不变,调用的是同一个函数 # 查看函数的位置参数的默认值 print(add.__defaults__) # 查看函数的关键字参数的默认值 print(add.__kwdefaults__) # 可以使用两种方法解决函数默认值带来的弊端: # 1:浅拷贝 def add(lst = []): lst = lst[:] lst.append('hello') print(lst) add() # 输出:['hello'] add() # 输出:['hello'] # 2:参数值判断 def add(lst=None): if lst is None: lst = [] lst.append(1) print(lst) add() print(add.__defaults__) add() print(add.__defaults__) add([1,2,3]) print(add.__defaults__) add([4,5,6]) print(add.__defaults__)
9:函数销毁
1:函数运行结束时销毁 2:del 删除函数对象 3:重命名覆盖函数对象 def add(): print('add') del add add()
1.什么是返回值
-
返回值指的是函数返回的结果;
-
return执行完毕后面的语句将不会再执行;
-
如果一个函数里面有两个return,前面return执行完毕,后面的return也不会执行;
2.函数的隐式返回和显示返回
-
Python函数都有返回值,如果有return语句,是显式返回;
-
如果没有return语句,默认返回None,是隐式返回;
3.函数多值返回
-
如果返回多个值通过逗号分开,会把值进行压缩,封装成一个元组;
-
如果返回一个列表,得到的就是一个列表;
4.什么是嵌套函数
-
嵌套函数的内部函数只能在包含它的函数的直接父级调用,也就是只能在包含它的外部函数中调用;
-
嵌套函数层数不宜过深,一般3层以内即可,太深不够直观,容易造成代码混乱;
5.函数作用域
-
作用域指的是,一个标识符的作用范围就是这个标识符的作用域;
-
在函数里面定义的变量(即局部作用域)只能在函数体内部被访问,函数外部不能访问;
-
在函数外部定义的变量(即全局作用域)能在函数体外部被访问,也能在函数体内部被访问;
6.函数的闭包
-
闭包:内部函数引用了外部函数的变量,这就是闭包的定义;
-
如果函数体想访问变量,只能在变量定义之后才能访问;
-
如果要访问函数体内部的函数,可以先把内部函数的函数名作为外部函数的返回值,把外部函数的引用赋值给变量,再调用变量;
7.关键字
-
global关键字:可以指定变量为全局变量,但是global关键字会污染全局变量,也就是会覆盖之前全局变量的值,所以最好慎用;
-
nonlocal关键字:可以申明内部函数的变量引用的是外部函数变量的值(作用域在外部函数),不是全局作用域的值,因此不会污染全局作用域;
8.函数默认值的作用域
-
同一个函数的生命周期相同,函数的默认值会绑定在函数的整个生命周期上,不会因为函数内部对默认值的操作而发生改变;
-
可以使用浅拷贝copy(简写[:])来清空默认值,那每次调用函数,默认值都为初始值;
-
也可以通过参数值判断来给默认值重新赋值,那每次调用函数,默认值都为初始值;
9.函数销毁
- 可以通过 del 函数名 的方式来删除函数,再调用函数时,就会报错不存在;
课后补充:
问题:闭包的具体使用?
答:闭包的理解就是在函数a里面再嵌套一个函数b等,那么函数b里面都可以引用函数a的变量,然后函数a返回值是函数b,这样就形成了闭包,这样就相当于说,这个函数b就是一个封闭的函数,只能在a函数中使用。
闭包的具体使用最好的例子就是后面的装饰器(21节:https://www.9xkd.com/user/plan-view.html?id=1603899338)
问题:嵌套函数的作用?
嵌套函数很方便,比如:一个函数里面有很多相同代码,但是其他地方不需要用,这个时候不需要定义公用函数,可以直接在这个地方定义一个内部函数,方便仅在当前这个函数里面公用;
子函数里面可以直接引用上层函数的变量,也可以直接引用全局变量;
嵌套还有一个好处就是,在各个函数里面定义的变量,另外的函数互相不会影响(嵌套一般也不要嵌套很多层,最好不超过3层);
问题:关于global关键字具体怎么使用呢?
答:global关键字的作用就是把局部变量修改为全局变量,它会覆盖原有的全局变量,所以我们要慎用global,能不用就不用,避免造成全局污染; 如下举个例子讲解一下,如下图所示:
fun2中变量c现在是局部变量,我们如果通过global声明c之后,就全局都可以调用c了,也就相当于c变成了全局变量;
fun3中,如果一个函数已经声明了一个局部变量c,并且赋值了999。然后在这个函数里面,再使用global 声明一个全局变量c,那么后面的子级函数和外部引用c都是引用的全局变量c的值;
但是如果我们在同级函数中引用c的话,引用的依旧是局部变量c的值。
是不是感觉有点乱,所以,我们为了避免混乱,建议不要声明了局部变量后,又吧它改为全局变量的形式。也就是说在同一级中,不要声明了局部变量c后,然后又声明全局变量c,如下,这样是错的:
问题:nonlocal关键字的具体使用?
答:如下图所示:nonlocal关键字用来标识变量所在的命名空间,用在嵌套函数中,表示外层;
在外层的变量a为全局变量,不属于嵌套函数,所以,用不能在函数外面使用nonlocal a,这样会报错(总之nonlocal不能引用全局变量);
在fun1中b属于嵌套函数中的外层函数的变量,所以,在fun2中使用nonlocal修改b是没有问题的;
nonlocal是可以隔层的,不一定需要是直接的父层,例如:我在fun1中定义的b,在fun3中的nonlocal也可以声明修改b,当然fun2中也可以修改,并且如果是在fun3中修改b,那使用时就会以fun2中的b的执行结果为初始值(也就是初始值不在是fun1中b的值了);
nonlocal的作用就是在嵌套函数中使用,对上级函数的变量进行申明修改;