1. 程式人生 > >第020講:函式:內嵌函式和閉包

第020講:函式:內嵌函式和閉包

目錄

測試題

0.如果希望在函式中修改全域性變數的值,應該使用什麼關鍵字?

1.在巢狀的函式中,如果希望在內部修改外部函式的區域性變數,應該使用什麼關鍵字?

2. Python 的函式可以巢狀,但要注意訪問的作用域問題哦,請問以下程式碼存在什麼問題呢?

3. 請問為什麼程式碼 A 沒有報錯,但程式碼 B 卻報錯了?應該如何修改?

4. 請問如何訪問 funIn() 呢?

5. 請問如何訪問 funIn() 呢?

6. 以下是“閉包”的一個例子,請你目測下會列印什麼內容?

動動手

0. 請用已學過的知識編寫程式,統計下邊這個長字串中各個字元出現的次數並找到小甲魚送給大家的一句話。

1. 請用已學過的知識編寫程式,找出小甲魚藏在下邊這個長字串中的密碼,密碼的埋藏點符合以下規律:

2. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!


測試題

0.如果希望在函式中修改全域性變數的值,應該使用什麼關鍵字?

global 關鍵字來自:bbs.fishc.com
v|MC#qQo%[email protected][.ij$6)A{?e"c
舉個例子:

>>> count = 5
>>> def MyFun():
                global count
                count = 10
                print(count)

>>> MyFun()
10
>>> count
10

1.在巢狀的函式中,如果希望在內部修改外部函式的區域性變數,應該使用什麼關鍵字?

nonlocal 關鍵字Powered by bbs.fishc.com
[email protected]^F%v]"7id[}I,AwL2ux
舉個例子:

>>> def Fun1():
                x = 5
                def Fun2():
                        nonlocal x
                        x *= x
                        return x
                return Fun2()

>>> Fun1()
25

2. Python 的函式可以巢狀,但要注意訪問的作用域問題哦,請問以下程式碼存在什麼問題呢?

def outside():
    print('I am outside!')
    def inside():
        print('I am inside!')

inside()

使用巢狀函式要注意一點就是作用域問題,inside() 函式是內嵌在 outside() 函式中的,所以 inside() 是人妻,除了身為老公的 outside() 可以碰(呼叫),在外邊或者別的函式體裡是無法對其進行呼叫的。

正確的呼叫應該是:

def outside():
    print('I am outside!')
    def inside():
        print('I am inside!')
        
    inside()
outside()

3. 請問為什麼程式碼 A 沒有報錯,但程式碼 B 卻報錯了?應該如何修改?

程式碼A:

def outside():
    var = 5
    def inside():
        var = 3
        print(var)
        
    inside()
outside()

程式碼B:

def outside():
    var = 5
    def inside():
        print(var)
        var = 3
        
    inside()
outside()

仔細一看報錯的內容是:UnboundLocalError: local variable 'var' referenced before assignment,說的是變數 var 沒有被定義就拿來使用,肯定錯啦!4ZW[J
M>D+afmER#J<c_]6NCb?9~
這裡 outside() 函式裡有一個 var 變數,但要注意的是,內嵌函式 inside() 也有一個同名的變數,Python 為了保護變數的作用域,故將 outside() 的 var 變數遮蔽起來,因此此時是無法訪問到外層的 var 變數的。%dFU6B*}
[email protected]'x6yP3d8Kw9zZ; m4Wu*nJcreH
應該修改為:

def outside():
    var = 5
    def inside():
        nonlocal var
        print(var)
        var = 3
        
    inside()
outside()

4. 請問如何訪問 funIn() 呢?

def funOut():
    def funIn():
        print('賓果!你成功訪問到我啦!')
    return funIn()

只需要直接呼叫 funOut() 即可:

funOut()
賓果!你成功訪問到我啦!

5. 請問如何訪問 funIn() 呢?

def funOut():
    def funIn():
        print('賓果!你成功訪問到我啦!')
    return funIn

區別於上一題,這裡你就需要用 funOut()() 訪問啦:S6yB

funOut()()
賓果!你成功訪問到我啦!

當然你也可以“曲線救國”:

go = funOut()
go()
賓果!你成功訪問到我啦!

6. 以下是“閉包”的一個例子,請你目測下會列印什麼內容?

def funX():
    x = 5
    def funY():
        nonlocal x
        x += 1
        return x
    return funY

a = funX()
print(a())
print(a())
print(a())

會列印:

6
7
8

動動手

0. 請用已學過的知識編寫程式,統計下邊這個長字串中各個字元出現的次數並找到小甲魚送給大家的一句話。

(由於我們還沒有學習到檔案讀取方法,大家下載後拷貝過去即可)

請下載字串檔案:string1.zip

str1 = '''拷貝過來的字串'''
list1 = []

for each in str1:
    if each not in list1:
        if each == '\n':
            print('\\n', str1.count(each))
        else:
            print(each, str1.count(each))
        list1.append(each)

1. 請用已學過的知識編寫程式,找出小甲魚藏在下邊這個長字串中的密碼,密碼的埋藏點符合以下規律:

    a) 每位密碼為單個小寫字母Powered by bbs.fishc.com
    b) 每位密碼的左右兩邊均有且只有三個大寫字母qH{]*ZR
Suj|vEA'h<^1eF]7i*(tbZU+$H yx
(由於我們還沒有學習到檔案讀取方法,大家下載後拷貝過去即可)9zk3R(q{
OtqD;C?r0kwco" R#A%BLTWFQ)
請下載字串檔案:string2.zip

str1 = '''ABSaDKSbRIHcRHGcdDIF'''

countA = 0  # 統計前邊的大寫字母
countB = 0  # 統計小寫字母
countC = 0  # 統計後邊的大寫字母
length = len(str1)

for i in range(length):
    if str1[i] == '\n':
        continue

    """
    |如果str1[i]是大寫字母:
    |-- 如果已經出現小寫字母:
    |-- -- 統計後邊的大寫字母
    |-- 如果未出現小寫字母:
    |-- -- 清空後邊大寫字母的統計
    |-- -- 統計前邊的大寫字母
    """
    if str1[i].isupper():
        if countB:
            countC += 1
        else:
            countC = 0
            countA += 1

    """
    |如果str1[i]是小寫字母:
    |-- 如果小寫字母前邊不是三個大寫字母(不符合條件):
    |-- -- 清空所有記錄,重新統計
    |-- 如果小寫字母前邊是三個大寫字母(符合條件):
    |-- -- 如果已經存在小寫字母:
    |-- -- -- 清空所有記錄,重新統計(出現兩個小寫字母)
    |-- -- 如果該小寫字母是唯一的:
    |-- -- -- countB記錄出現小寫字母,準備開始統計countC
    """
    if str1[i].islower():
        if countA != 3:
            countA = 0
            countB = 0
            countC = 0
        else: 
            if countB:
                countA = 0
                countB = 0
                countC = 0
            else:
                countB = 1
                countC = 0
                target = i

    """
    |如果前邊和後邊都是三個大寫字母:
    |-- 如果後邊第四個字母也是大寫字母(不符合條件):
    |-- -- 清空記錄B和C,重新統計
    |-- 如果後邊僅有三個大寫字母(符合所有條件):
    |-- -- 列印結果,並清空所有記錄,進入下一輪統計
    """
    if countA == 3 and countC == 3:
        if i+1 != length and str1[i+1].isupper():
            countB = 0
            countC = 0
        else:
            print(str1[target], end='')
            countA = 3
            countB = 0
            countC = 0

2. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!d

這節課主要講解內嵌函式和閉包

(1)glabal 關鍵字

將函式體內部的區域性變數變為全域性變數。

(2)內嵌函式(內部函式)

需要注意的是內嵌函式的整個作用域,都在外部函式之內的,也就是說,出了外部函式的範圍,是沒有辦法呼叫內部函式的。即:假設函式 fun2()的定義和呼叫的過程都在函式 fun1()內,那麼除了在 fun1() 這個函式體裡面可以隨便呼叫 fun2()外,出了fun1(),就沒有任何可以對fun2()呼叫的方式了。

(3)閉包

閉包是函數語言程式設計重要的語法結構。不同的程式語言實現閉包的形式不同,Python中閉包從表現形式上定義為:如果在一個內部函式裡,對外部作用域(但不是在全域性作用域)的變數進行引用,那麼內部函式就被認為是閉包。例如:

對於函式FunX(),內部函式為FunY(),內部函式的外部作用域就是FunX()的整個函式空間,的變數就是x,對它進行引用了,達到這個要求了,所以就是內部函式FunY()就是一個閉包。那如何進行呼叫呢?

首先我們發現呼叫FunX()會得到一個函式,i是函式FunX()的函式,如果需要得到x*y的值,我們還需要對FunY()進行呼叫。

使用閉包需要注意的是:因為閉包的概念是由內部函式演變而來,所以你也不能在外部函式的外邊對內部函式進行呼叫。在閉包中,外部函式的區域性變數對內部函式的區域性變數就相當於之前的全域性變數之於區域性變數的關係。在內部函式中,你只能對外部函式的區域性變數進行訪問,但你不能對它進行修改。例如:

因為這裡的 x *= x 中的x被認為是一個區域性變數,且沒有被定義,所以報錯。如果需要在區域性使用全域性的變數,需要用到nonlocal關鍵字,這個關鍵字的使用方法與global相同。