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,最后打印這句話。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM