Python之路Python全局变量与局部变量、函数多层嵌套、函数递归
一、局部变量与全局变量
1、在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
全局变量没有任何缩进,在任何位置都可以调用。
子程序:如用def定义的函数。
作用域
一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域
全局作用域(global):在整个程序运行环境中都可见
局部作用域:在函数、类等内部可见;局部变量使用范围不能超过其所在的局部作用域。
例子
NAME = "nicholas" def change_NAME(): print("change_NAME", NAME) change_NAME() print(NAME)
输出结果
change_NAME nicholas nicholas
分析:NAME = "nicholas"就是全局变量,在
change_NAME()函数体内可以直接调用打印出“change_NAME nicholas”
2、当全局变量与局部变量同名时:
在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。
例子:
NAME = "nicholas" def change_NAME(): NAME = "niubi" print("change_NAME", NAME) change_NAME() print(NAME)
输出结果
change_NAME niubi nicholas
分析:当全局变量与局部变量同名时:在 def change_NAME():函数内部,
执行print("change_NAME", NAME)语句时,这里的NAME优先调用函数内部的值,函数执行结束后执行print(NAME)语句,全局变量NAME = "nicholas"起作用。
3、如果函数内部无global关键字
优先读取局部变量,如果没有局部变量则读取全局变量,此时无法对全局变量进行赋值。
但是对于可变对象可以对内部元素进行操作(如append()pop()).
大前提:无global关键字
a、有声明(同名)局部变量
例子
name = ["pony","jack"] print(1,name) def change_name(): name = "nicholas" print("change_name", name) change_name() print(2,name)
输出结果
1 ['pony', 'jack'] change_name nicholas 2 ['pony', 'jack']
1 ['pony', 'jack'] change_name nicholas 2 ['pony', 'jack']
分析:这里无golbal关键字,执行 print(1,name)语句时读取全局变量name = ["pony","jack"],之后执行change_name()函数,在函数里面nema被赋值为"nicholas"
执行print("change_name", name)语句,这里的name优先读取函数内部的局部变量name = "nicholas"
输出change_name nicholas。之后change_name()函数结束。
执行print(2,name)语句。这里仍然读取全局变量name = ["pony","jack"]。
b、无声明(同名)局部变量
例子
name = ["pony","jack"] print(1,name) def change_name(): print(3, name) name.append("nicholas") print(4,name) change_name() print(2,name)
输出结果
1 ['pony', 'jack'] 3 ['pony', 'jack'] 4 ['pony', 'jack', 'nicholas'] 2 ['pony', 'jack', 'nicholas']
分析:无global关键字,针对全局变量如果是可变对象,可以对内部元素进行操作。
4、如果函数中有global关键字,变量本质上就是全局变量,可读取可赋值。
a、有声明(同名)局部变量
例子
NAME = "nicholas" print(1,NAME) def change_NAME(): global NAME NAME = "niubi" print("change_NAME", NAME) change_NAME() print(2,NAME)
输出结果
1 nicholas change_NAME niubi 2 niubi
分析:在执行print("1",NAME)语句时,NAME使用全局变量,然后执行change_NAME()函数,在函数内部有global关键词声明,之后执行NAME = "niubi",执行完这句之后整个程序的NAME变量的值就被修改为"niubi"。
继续输出change_NAME niubi,函数结束。
最后执行print("2",NAME)语句,由于NAME在函数内部被修改为"niubi",所以这里输出
2 niubi
例子2
name = ["pony","jack"] print(1,name) def change_name(): global name name = ["nick"] name.append("nicholas") print(3,name) change_name() print(2,name)
输出结果
1 ['pony', 'jack'] 3 ['nick', 'nicholas'] 2 ['nick', 'nicholas']
分析:
开始name = ["pony","jack"]是全局变量,之后执行change_name()函数吗,在函数中有global关键字,之后针对name做的修改都相当于将name作为全局变量来修改。
c、注意global的位置
错误例子
分析:如果需要global对全局变量进行修改这里的global不能放在name = "nick"下面。
5、代码规范:全局变量字母全部大写,局部变量变量名小写。
二、多层函数的嵌套和作用域
(1)一定要注意函数要先定义,后使用
例子1
def test1(): print("test1") def test2(): print("test2") test1() test2()
分析:这样是可以的,先定义函数,再使用函数
错误例子
def test1(): print("test1") test2() def test2(): print("test2") test1()
分析:这样的test2()就无法执行。
(2)在函数内定义的函数 在外面不能用到
例子2
def outer(): def inner(): print('inner') print('outer') inner() outer()
分析:函数有可见范围,这就是作用域的概念。内部函数不能被外部直接使用。
例子
def foo(): print("foo") too() def too(): print("too") foo()
分析:这里执行顺序是加载def foo():
加载def too():然后再执行foo(),所以这里不会报错。
(3)分析多层嵌套函数执行过程及结果
例子
NAME = 'nicholas' def jack(): name = "jack" print(name) def pony(): name = "pony" print(name) def charles(): name = 'charles' print(name) print(name) charles() pony() print(name) jack()
输出结果:
jack pony pony charles jack
分析:
执行过程如下图
执行顺序:1----2----3----3.1----3.2----3.3----3.4----3.3.1----
3.3.2----3.3.3----3.3.4----3.3.5--3.3.3.1--3.3.3.2----3.5
1 首先执行NAME = 'nicholas'语句,
2 加载def jack():函数到内存进行编译,但不执行
3 调用jack()函数,开始执行
3.1 执行name = "jack"语句
3.2 执行print(name)语句,这里由于没有global关键字,优先读取局部变量name = "jack",所以这里输出jack
3.3 加载def pony():函数到内存进行编译,但不执行
3.4 调用pony():函数,开始执行
3.3.1 执行name = "pony"语句,这里是一个局部变量
3.3.2 执行print(name)语句,这里由于没有global、nonlocal关键字,优先读取局部变量name = "pony",所以这里输出pony
3.3.3 加载charles():函数到内存进行编译,但不执行
3.3.4 执行print(name)语句,这里由于没有global、nonlocal关键字,优先读取同一层级的局部变量name = "pony",所以这里输出pony
3.3.5 调用charles():函数,开始执行
3.3.3.1 执行name = 'charles'语句,这里是个局部变量
3.3.3.2 执行print(name)语句,优先读取局部变量name = "charles",所以这里输出charles
~~charles():函数结束
~~pony():函数
3.5 执行执行print(name)语句,优先使用同层级的局部变量name = "jack",所以这里输出jack。
~~整体结束
例子
name = "nicholas" def outer(): name = "nick" def inner(): print(name) print(name) inner() outer()
输出结果
nick nick
分析:注意这里的inner()函数内部的print(name)语句,这里仍然是优先使用outer()函数内部的局部变量name = "nick",而非全局变量。
(4)
nonlocal关键词
nonlocal,指定上一级变量,如果没有就继续往上直到找到为止
例子
看这个程序,分析输出过程和结果。
def scope_test(): def do_local(): spam = "local spam" def do_nonlocal(): nonlocal spam spam = "nonlocal spam" def do_global(): global spam spam = "global spam" spam = "test spam" do_local() print("After local assignment:", spam) do_nonlocal() print("After nonlocal assignment:", spam) do_global() print("After global assignment:", spam) scope_test() print("In global scope:", spam)
输出结果
After local assignment: test spam After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam
分析:
程序执行步骤如图
从1开始
1--2--2.1--2.2--2.3--2.4--2.5--2.5.1--2.5.2--2.6--2.7--2.7.1--2.7.2--2.8
--2.9--2.9.1--2.9.2--2.10--2.11
下面具体分析下程序执行的过程
1 将def scope_test():函数体作为一个整体加载到内存中,但不执行
2 调用def scope_test():开始执行
2.1 将def do_local():函数体作为一个整体加载到内存中,但不执行
2.2 将def do_nonlocal(): 函数体作为一个整体加载到内存中,但不执行
2.3 将 def do_global(): 函数体作为一个整体加载到内存中,但不执行
2.4 执行 spam = "test spam"
2.5 调用 def do_local():函数
2.5.1 执行 def do_local():函数
2.5.2 执行 spam = "local spam"
--完成2.5.2之后 def do_local():函数结束,其所占的内存被回收, spam =
"local spam"数据被销毁
2.6 执行print("After local assignment:", spam)语句
由于没有global关键字,这里优先读取局部变量,即spam = "test spam"
打印出After local assignment: test spam
2.7 调用do_nonlocal()函数
2.7.1 执行def do_nonlocal():
遇到 nonlocal 声明,nonlocal关键字用来在函数外层(非全局)变量。
这里的外层即为def scope_test():这个作用域内
2.7.2 执行spam = "nonlocal spam"语句
这时def scope_test():这个作用域内由以前的spam = "test spam"被重新覆盖为
spam = "nonlocal spam"
--do_nonlocal()函数体结束
2.8 执行 print("After nonlocal assignment:", spam)语句
由于spam被重新赋值为"nonlocal spam",这里输出
After nonlocal assignment: nonlocal spam
2.9 调用do_global()函数
2.9.1 执行def do_global(): 函数
2.9.2 执行 spam = "global spam" 语句
遇到global声明,global关键字用来在函数整体作用域使用全局变量。类似于在
def scope_test():上面写了一句spam = "global spam"
--def do_global(): 函数体结束
2.10 执行print("After global assignment:", spam)语句
由于这一层级作用域没有global关键字,这里优先读取局部变量,即被修改过一次的
spam = "nonlocal spam"
这里输出After global assignment: nonlocal spam
2.11执行print("In global scope:", spam)语句
由于在2.9.2 spam被声明了全局变量,即spam = "global spam"
所以这里输出
In global scope: global spam
例子2
name = "jack" def foo(): name = "nick" print(name) def too(): nonlocal name name = "nicholas" print(1,name) too() print(name) foo()
输出结果
nick 1 nicholas nicholas
分析:注意这里的def too():函数内print(1,name)语句仍然优先读取局部变量name = "nicholas"。
三、递归
1、递归的定义
如果在调用一个函数的过程中直接或间接调用自身本身,那么这种方法叫做递归。
2、递归的特点
a、递归必须有一个明确的结束条件(基例)。
b、每次进入更深一层递归时,问题规模相比上次递归都应有所减少。
c、递归效率不高,递归层次过多会导致栈溢出。
3、递归的执行过程
例子
def calc(n): print(n) if int(n/2) ==0: return n return calc(int(n/2)) calc(10)
输出结果
10 5 2 1
分析执行过程:
具体过程
(1)执行def calc(n):语句,将calc(n)函数加载到内存中进行编译,但不执行
(2)执行calc(10)语句,调用calc(n)函数,将n = 10 传入calc(n)函数
(3)执行print(n)语句,此时n = 10,打印10
判断n/2是否等于0,10/2 = 5不等于0
执行retun语句,return调用calc(n)函数,
此时具体是执行calc(int(10/2))即calc(5)
此层函数暂停,等待calc(5)返回值
(4)执行print(n)语句,此时n = 5,打印5
判断n/2是否等于0,5/2 = 2不等于0
执行retun语句,return调用calc(n)函数,
此时具体是执行calc(int(5/2))即calc(2)
此层函数暂停,等待calc(2)返回值
(5)执行print(n)语句,此时n = 2,打印2
判断n/2是否等于0,2/2 = 1不等于0
执行retun语句,return调用calc(n)函数,
此时具体是执行calc(int(2/2))即calc(1)
此层函数暂停,等待calc(1)返回值
(6)执行print(n)语句,此时n = 1,打印1
判断n/2是否等于0,1/2 = 2等于0,
执行if条件下的retun语句,return n 给上一层函数,
即return 1给上层函数
(7)将1传给calc(1),calc(1)得到值为1 ,
return calc(1)即return 1,再次将1传给上层的return calc(2),
calc(2)得到值为1,再次将1传给上层的return calc(5),
calc(5)得到值为1,最后将1传给calc(10),
即calc(10)= 1。
这里可以打印下calc(10)的值
def calc(n): print(n) if int(n / 2) == 0: return n return calc(int(n / 2)) v = calc(10) print("calc(10)是",v)
输出结果
10 5 2 1 calc(10)是 1
例子2
import time person_list=['Pony','Charles','Richard ','Jack'] print("How can I make good money?") def ask(person_list): print('-'*60) if len(person_list) == 0: return "I don't know" person=person_list.pop(0) if person == "Jack": return "%s say:Better have a dream, in case it comes true someday." %person print('hi Boss[%s],How can I make good money?' %person) print("%s replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask %s..." %(person,person_list)) time.sleep(10) res=ask(person_list) #print('%s say: %res' %(person,res))#注释语句 return res res = ask(person_list) print(res)
输出结果
How can I make good money? ------------------------------------------------------------ hi Boss[Pony],How can I make good money? Pony replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Charles', 'Richard ', 'Jack']... ------------------------------------------------------------ hi Boss[Charles],How can I make good money? Charles replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Richard ', 'Jack']... ------------------------------------------------------------ hi Boss[Richard ],How can I make good money? Richard replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Jack']... ------------------------------------------------------------ Jack say:Better have a dream, in case it comes true someday.
如果取消上面print('%s say: %res' %(person,res))注释,执行这一语句,可以看出return返回的过程
如下
import time person_list=['Pony','Charles','Richard ','Jack'] print("How can I make good money?") def ask(person_list): print('-'*60) if len(person_list) == 0: return "I don't know" person=person_list.pop(0) if person == "Jack": return "%s say:Better have a dream, in case it comes true someday." %person print('hi Boss[%s],How can I make good money?' %person) print("%s replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask %s..." %(person,person_list)) time.sleep(1) res=ask(person_list)#第一处 print('%s say: %res' %(person,res)) return res res = ask(person_list)#第二处 print(res)
输出结果
How can I make good money? ------------------------------------------------------------ hi Boss[Pony],How can I make good money? Pony replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Charles', 'Richard ', 'Jack']... ------------------------------------------------------------ hi Boss[Charles],How can I make good money? Charles replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Richard ', 'Jack']... ------------------------------------------------------------ hi Boss[Richard ],How can I make good money? Richard replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Jack']... ------------------------------------------------------------ Richard say: 'Jack say:Better have a dream, in case it comes true someday.'es Charles say: 'Jack say:Better have a dream, in case it comes true someday.'es Pony say: 'Jack say:Better have a dream, in case it comes true someday.'es Jack say:Better have a dream, in case it comes true someday.
分析:最后的返回结果是Richard返回给Charles,Charles返回给Pony
第一处的res=ask(person_list) 就算执行完了,res得到Jack say:Better have a dream, in case it comes true someday.
然后return给函数外的res,最后打印这句话。