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']

  

分析:这里无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的位置

 

错误例子

name = ["pony","jack"]
def change_name():
    name = "nick"
    global name
    name.append("nicholas")
change_name()
print(name)

  

分析:如果需要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,最后打印这句话。

posted on 2018-03-20 22:49  Nicholas--  阅读(815)  评论(0编辑  收藏  举报

导航