Python之路Python全局变量与局部变量、函数多层嵌套、函数递归

时间:2020-11-26 06:50:13

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

分析:

执行过程如下图

Python之路Python全局变量与局部变量、函数多层嵌套、函数递归

执行顺序: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

  

分析:
程序执行步骤如图

Python之路Python全局变量与局部变量、函数多层嵌套、函数递归

从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

分析执行过程:

Python之路Python全局变量与局部变量、函数多层嵌套、函数递归

具体过程

(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,最后打印这句话。