这一篇教程,我们先来看一段代码。
示例代码:
x = 0 # 全局变量 def outside(): # 定义函数 x = 1 # 局部变量,内嵌函数的外部变量 def inside(): # 定义内嵌函数 x = 2 # 局部变量 return x return x, inside # 将变量值和函数返回 o, i = outside() # 通过两个变量接收outside函数的返回值x和inside print(x) # 显示输出结果为:0 print(o) # 显示输出结果为:1 print(i()) # 显示输出结果为:2
刚才的这段代码,可能不太容易理解。
因为里面包含了几部分我们没有接触过的内容。
第一部分,我们先来了解一下函数嵌套。
在Python中可以在函数的内部再定义函数。
大家能够看到,在上方代码中函数outside的内部,又定义了一个函数inside。
这种结构就是函数嵌套。
另外,在上方代码中,大家能够看到函数的返回值不仅可以返回多个,而且可以返回内嵌函数(这是闭包,后面会讲到)。
返回内嵌函数时,如果函数名称后方没有加上“()”,调用外层函数时不会立即执行返回的函数,需要在调用外层函数后,添加“()”来执行。例如,上方代码最后一句中的“i()”,就是执行变量中保存的函数。
而且,我们也可以用下面这种方法去执行返回的函数。
示例代码:(接上一段代码)
outside()[1]() # 调用外层函数时,获取的返回值列表中第2项是函数,加上()则会被执行。
这是外层函数有多个返回值时的方法,通过对外层函数返回值列表进行索引,找到函数执行。
如果,外层函数只有一个返回值,我们可以通过函数名称后方直接加上“()()”去执行返回的函数。
另外,在外层函数返回内嵌函数时,在函数名称后方加上了“()”,会在调用外层函数时自动执行。
大家可以通过运行以下代码进行对比。
示例代码:(返回函数不带括号)
def outside(): print('执行外层函数!') def inside(): print('执行内嵌函数!') return inside outside()
示例代码:(返回函数带有括号)
def outside(): print('执行外层函数!') def inside(): print('执行内嵌函数!') return inside() outside()
第二部分,我们来了解变量的作用域。
在上方代码中,我们能够看到有3个变量的名称都是x。
这3个x并非同一个变量,只是名称一样,它们的特点可以这么理解:
- 最外层的变量x是全局变量
- 函数outside中和inside定义的变量x,都是局部变量。
- 函数outside中的变量x,相对于内嵌的函数inside,它是外部变量。
这3个变量有着不同的作用域。
如上图所示,3个x变量的作用域如下:
- 最外层的变量x,它的作用域是整个模块中。
- 函数outside中的变量x,作用域是函数outside内部以及内嵌函数inside。
- 函数inside中的变量x,作用域是函数inside内部。
也就是说,全局变量作用域是当前模块内,而局部变量作用域是函数以及内嵌函数中。
但是,这里大家可能会有疑问,既然全局变量作用域是全局的,那么在函数中又创建同名变量不是冲突了吗?
这个不用担心,当在函数中创建与全局变量同名的局部变量时,在函数内部会自动屏蔽同名的全局变量。
所以,在本文开头的代码中,显示输出的结果并不相同。
下图就是3个变量x的值输出显示时的过程。
到这里,我们有必要来了解一下作用域的概念。
作用域也叫命名空间,命名空间是一个我们看不到的字典,字典的键记录变量的名称,字典的值记录着变量的值。
每个模块都会创建一个全局命名空间,用于记录模块的变量,包括全局变量和局部变量,以及其它导入模块中的变量。
每个函数调用时都会创建一个局部命名空间,用于记录函数的变量,包括函数的参数和函数内部创建的变量。
另外,还有内置命名空间,任何模块都能够访问,记录了内置函数和异常。
第三部分,我们来了解如何在函数中读取与修改全局变量。
首先,我们先来看读取全局变量。
一般情况下,我们在函数中可以直接读取全局变量。
这里我们看一段根据半径计算圆形周长的代码。
示例代码:
radius = 21 # 创建半径的变量并赋值 def get_perimeter(): # 定义获取周长的函数 perimeter = round(2 * 3.14 * radius,2) # 创建周长的变量保存计算结果 return perimeter # 返回结果 print(get_perimeter()) # 显示输出结果为:131.88
在上方代码中,我们定义了全局变量radius,在函数中我们可以直接调用这个变量参与计算。
但是,如果我们在函数中创建的局部变量如果与全局变量同名,这时候怎么调用全局变量呢?
我们可以借用内置函数globals来获取到全局变量。
示例代码:
def global_case(): # 定义函数 x = 5 # 创建同名局部变量 result = x * globals()['x'] # 通过globals函数获取全局变量x与局部变量x相乘 return result # 返回结果 print(global_case()) # 显示输出结果为:15
接下来,我们来看如何在函数中修改全局变量(或者叫重新绑定全局变量为其他值)。
当我们创建了某个全局变量,在函数中无法直接修改这个变量。
就像之前的代码中,即便在函数中给x赋值(例如x=1),也只是创建了一个新的局部变量,而不是对全局变量x进行修改。
那么,如何在函数中修改全局变量呢?
我们可以使用global关键字,声明要重新绑定的全局变量。
这样,Python解释器就能够知道,我们是要对已有的全局变量进行修改,而不是创建一个新的同名局部变量。
示例代码:
x = 10 # 创建全局变量 def change_global(): # 定义函数 global x # 声明要修改的全局变量 x = 5 # 修改变量值 change_global() # 调用函数 print(x) # 显示输出全局变量x的值,结果为:5
在上方代码中,大家能够看到,我们定义了全局变量x和修改这个全局变量的函数change_global。
在修改全局变量的函数change_global中我们并没有设定返回值,它只起到修改的作用。
所以,当我们执行了这个函数,再显示输出变量x的值,就是经过修改后的值。
补充说明:如果想在内嵌函数中修改外层函数中的局部变量,可以使用关键字nonlocal进行声明。使用方法和关键字global类似。
示例代码:
def nonlocal_case(): x = 10 # 创建局部变量 def change_nonlocal(): # 定义函数 nonlocal x # 声明要修改的外部变量 x = 5 # 修改变量值 change_nonlocal() # 调用修改函数执行修改 return x # 返回变量x的值 print(nonlocal_case()) # 显示输出全局变量x的值,结果为:5
第四部分,我们来了解一下闭包(closure)的概念。
在网上查了闭包的解释,百度百科中是这样说的:闭包由要执行的代码块(内嵌函数)和为自由变量(外部变量)提供绑定的计算环境(作用域/命名空间)组成。
注意,上面这段话中,括号里面的内容是我按照自己的理解添加的。
以我个人的观点,闭包就是由返回的内嵌函数和这个内嵌函数中用到的外部变量(1)以及其他函数(2)打包而成的一个整体。
(1)外部变量,包括:外层函数的参数变量以及外层函数中定义的局部变量
(2)其它函数,包括:外层函数中定义的其他内嵌函数
例如,在本文第一段代码中就有闭包的出现。
为了更清楚的解释闭包的概念,我们来看一段代码。
用户输入合成的宝石数量,然后执行合成计算。
示例代码:
def synthesis(): count = int(input('请放入宝石:')) while True: if count < 3: count += int(input('宝石太少,请再放入一些宝石:')) else: break def execute(): result = count // 3 # 调用外部变量进行整除运算 print('您放入了%s颗宝石,合成了%s颗高级宝石!' % (count,result)) return execute exe = synthesis() # 调用函数 print('------------------开始合成------------------') exe() #执行闭包内容
这段代码中,闭包包含的内容,如下图所示。
如图所示,外层函数被调用,执行return语句时,会将内嵌函数返回,形成闭包存入变量exe。
exe中的闭包内容包括:外层函数中变量count和内嵌函数excute。
exe()语句会执行闭包中的内容,我们可以认为执行了以下语句。
count = * # "*"表示外层函数执行后,变量count的最终值 def execute(): result = count // 3 # 调用外部变量进行整除运算 print('您的%s颗宝石,合成了%s颗高级宝石!' % (count, result))
本节知识点:
1、嵌套函数;
2、作用域的概念;
3、函数中对外部变量与全局变量的修改;
4、闭包的概念。
本节英文单词与中文释义:
1、inside:内部
2、outside:外部
3、radius:半径
4、perimeter:周长
5、case:情形
6、synthesis:合成
7、execute:执行
练习:独立完成宝石合成的示例代码。
转载请注明:魔力Python » Python3萌新入门笔记(20)