1. 程式人生 > 其它 >15.函式的返回值與巢狀函式

15.函式的返回值與巢狀函式

(十七)函式的返回值與巢狀函式

1:什麼是返回值

    返回值指的是函式返回的結果
    # 示例1
    def add(x, y): 
        print(x, y)
        return x + y   # 返回值, return以後的語句將不會再執行
        print(x)
        print(y)
    add(1,2)
    # 示例2
    def add(x, y):
        print(x, y)
        return x + y   # 函式體內,碰到return語句,函式執行完畢,之後的語句將不會再執行
        
return x - y # 不會再次執行 add(1,2)

2:函式的隱式返回和顯示返回

    Python函式都有返回值,如果函式體內沒有return語句,那麼預設返回None,稱為隱式返回
    如果有return語句,直接返回,稱為顯示返回

3:函式多值返回

    函式可以返回多個值
    def get_data(x,y,z):
        return x + 1, y + 1, z + 1
    print(get_data(1,2,3))  # 得到的是一個元組
    def get_data(x,y,z):
        return
[x + 1, y + 1, z + 1] print(get_data(1,2,3)) # 得到的是一個列表

4:什麼是巢狀函式

    def outer_function():
        # 內部函式
        def inner_function():
            print("inner_function")
        print("outer_function")
        # 內部呼叫
        inner_function()
    outer_function()
    注意:inner_function函式只能在outer_function函式內部呼叫,無法再outer_function函式外部被呼叫

5:函式作用域

    什麼是作用域:作用域指的是,一個識別符號的作用範圍就是這個識別符號的作用域
    # 示例1: z變數只能在函式內部被訪問無法在函式外部被訪問,z變數的作用域就是add函式內部
    def add(x, y):
        z = 100
        print(x, y , z)
    add(1,2)
    print(z)
    # 示例2:z可以在add函式外部被訪問到,
    z = 100
    def add(x, y):
        print(x, y , z)
    add(1,2)
    print(z)
    小結:
    全域性作用域:整個執行環境中都可見,也就是整個模組內可見
    區域性作用域:函式或類中可見,函式體或類就是作用域

6:函式的閉包

    # 對比下列中的函式
    # 示例1
    x=5
    def add():
        y = x + 1
    add()
    # 示例2
    x=5
    def add():
        x = x + 1  # 報錯:local variable 'x' referenced before assignment
    add()
    報錯原因分析:
        在示例1中,函式內部引用的x為函式外部的x, 因此x的值為5
        在示例2中,函式內部重新定義了x的值,那麼整個函式內部都會使用這個內部x,因此在運算x + 1的時候,x還沒有完成定義就被引用了,這裡的x引用的一定是內部正在定義的x,不是函式外部的x=5
    # 示例3:
    x=5
    def add():
        print(x) # 這裡的x引用的是x = 1的x,但是執行print(x)時,x還沒有被定義,所以報錯:local variable 'x' referenced before assignment
        x =  1
    add()
    # 4 閉包: 內部函式引用了外部函式的變數,這就是閉包的定義
    def outer_function():
        x = 100
        def inner_function():
            print(x)        # 內部函式引用了外部函式的自由變數
        return inner_function
    ret = outer_function()
    ret()

7:nonlocal關鍵字、global關鍵字

    # 1:global關鍵字
    x=5
    def add():
        global x  # 使用global關鍵字,指定x的引用全域性的x變數
        x = x + 1
    add()
    # global關鍵字對全域性變數的汙染,因此需要慎用
    x = 100
    def foo():
        global x  # x被聲明瞭全域性變數
        x = 10
        x += 1
        print(x)  # x的值為11
    foo()
    print(x)      # x的值為11, 因此global關鍵字汙染了全域性變數x
    # 2:nonlocal關鍵字
    def outer_function():
        x = 100
        def inner_function():
            x = x + 1       # 這樣會報錯
            print(x)
        return inner_function
    ret = outer_function()
    ret()
    # 修改上面的程式碼,使用nonlocal關鍵字
    def outer_function():
        x = 100
        def inner_function():
            nonlocal x
            x = x + 1       # 這樣會報錯
            print(x)
        return inner_function
    ret = outer_function()
    ret()
    注意:nonlocal關鍵字的意義在於:內部函式的變數引用的外部函式變數的值,不是全域性作用域的值,因此不會汙染全域性作用域

8:函式預設值的作用域

    # 示例, 函式的預設值繫結在函式物件的整個生命週期,不會因為函式內部對預設值操作而發生改變
    def add(lst = []):
        lst.append('hello')
        print(lst)
    add() # 輸出:['hello']
    print('id={}'.format(id(add)))   # 函式物件的id值不變,呼叫的是同一個函式
    add() # 輸出['hello', 'hello']
    print('id={}'.format(id(add)))   # 函式物件的id值不變,呼叫的是同一個函式
    # 檢視函式的位置引數的預設值
    print(add.__defaults__)
    # 檢視函式的關鍵字引數的預設值
    print(add.__kwdefaults__)
    # 可以使用兩種方法解決函式預設值帶來的弊端:
    # 1:淺拷貝
    def add(lst = []):
        lst = lst[:]
        lst.append('hello')
        print(lst)
    add() # 輸出:['hello']
    add() # 輸出:['hello']
    # 2:引數值判斷
    def add(lst=None):
        if lst is None:
            lst = []
        lst.append(1)
        print(lst)
    add()
    print(add.__defaults__)
    add()
    print(add.__defaults__)
    add([1,2,3])
    print(add.__defaults__)
    add([4,5,6])
    print(add.__defaults__)

9:函式銷燬

    1:函式執行結束時銷燬
    2:del 刪除函式物件
    3:重新命名覆蓋函式物件
    def add():
        print('add')
    del add
    add()

1.什麼是返回值

  • 返回值指的是函式返回的結果;

  • return執行完畢後面的語句將不會再執行;

  • 如果一個函式裡面有兩個return,前面return執行完畢,後面的return也不會執行;

2.函式的隱式返回和顯示返回

  • Python函式都有返回值,如果有return語句,是顯式返回;

  • 如果沒有return語句,預設返回None,是隱式返回;

3.函式多值返回

  • 如果返回多個值通過逗號分開,會把值進行壓縮,封裝成一個元組;

  • 如果返回一個列表,得到的就是一個列表;

4.什麼是巢狀函式

  • 巢狀函式的內部函式只能在包含它的函式的直接父級呼叫,也就是隻能在包含它的外部函式中呼叫;

  • 巢狀函式層數不宜過深,一般3層以內即可,太深不夠直觀,容易造成程式碼混亂;

5.函式作用域

  • 作用域指的是,一個識別符號的作用範圍就是這個識別符號的作用域;

  • 在函式裡面定義的變數(即區域性作用域)只能在函式體內部被訪問,函式外部不能訪問;

  • 在函式外部定義的變數(即全域性作用域)能在函式體外部被訪問,也能在函式體內部被訪問;

6.函式的閉包

  • 閉包:內部函式引用了外部函式的變數,這就是閉包的定義;

  • 如果函式體想訪問變數,只能在變數定義之後才能訪問;

  • 如果要訪問函式體內部的函式,可以先把內部函式的函式名作為外部函式的返回值,把外部函式的引用賦值給變數,再呼叫變數;

7.關鍵字

  • global關鍵字:可以指定變數為全域性變數,但是global關鍵字會汙染全域性變數,也就是會覆蓋之前全域性變數的值,所以最好慎用;

  • nonlocal關鍵字:可以申明內部函式的變數引用的是外部函式變數的值(作用域在外部函式),不是全域性作用域的值,因此不會汙染全域性作用域;

8.函式預設值的作用域

  • 同一個函式的生命週期相同,函式的預設值會繫結在函式的整個生命週期上,不會因為函式內部對預設值的操作而發生改變;

  • 可以使用淺拷貝copy(簡寫[:])來清空預設值,那每次呼叫函式,預設值都為初始值;

  • 也可以通過引數值判斷來給預設值重新賦值,那每次呼叫函式,預設值都為初始值;

9.函式銷燬

  • 可以通過 del 函式名 的方式來刪除函式,再呼叫函式時,就會報錯不存在;

課後補充:

問題:閉包的具體使用?

答:閉包的理解就是在函式a裡面再巢狀一個函式b等,那麼函式b裡面都可以引用函式a的變數,然後函式a返回值是函式b,這樣就形成了閉包,這樣就相當於說,這個函式b就是一個封閉的函式,只能在a函式中使用。

閉包的具體使用最好的例子就是後面的裝飾器(21節:https://www.9xkd.com/user/plan-view.html?id=1603899338

問題:巢狀函式的作用?

巢狀函式很方便,比如:一個函式裡面有很多相同程式碼,但是其他地方不需要用,這個時候不需要定義公用函式,可以直接在這個地方定義一個內部函式,方便僅在當前這個函式裡面公用;

子函式裡面可以直接引用上層函式的變數,也可以直接引用全域性變數;

巢狀還有一個好處就是,在各個函式裡面定義的變數,另外的函式互相不會影響(巢狀一般也不要巢狀很多層,最好不超過3層);

問題:關於global關鍵字具體怎麼使用呢?

答:global關鍵字的作用就是把區域性變數修改為全域性變數,它會覆蓋原有的全域性變數,所以我們要慎用global,能不用就不用,避免造成全域性汙染; 如下舉個例子講解一下,如下圖所示:

fun2中變數c現在是區域性變數,我們如果通過global宣告c之後,就全域性都可以呼叫c了,也就相當於c變成了全域性變數;

fun3中,如果一個函式已經聲明瞭一個區域性變數c,並且賦值了999。然後在這個函式裡面,再使用global 宣告一個全域性變數c,那麼後面的子級函式和外部引用c都是引用的全域性變數c的值;

但是如果我們在同級函式中引用c的話,引用的依舊是區域性變數c的值。

是不是感覺有點亂,所以,我們為了避免混亂,建議不要聲明瞭區域性變數後,又吧它改為全域性變數的形式。也就是說在同一級中,不要聲明瞭區域性變數c後,然後又宣告全域性變數c,如下,這樣是錯的:

問題:nonlocal關鍵字的具體使用?

答:如下圖所示:nonlocal關鍵字用來標識變數所在的名稱空間,用在巢狀函式中,表示外層;

在外層的變數a為全域性變數,不屬於巢狀函式,所以,用不能在函式外面使用nonlocal a,這樣會報錯(總之nonlocal不能引用全域性變數);

在fun1中b屬於巢狀函式中的外層函式的變數,所以,在fun2中使用nonlocal修改b是沒有問題的;

nonlocal是可以隔層的,不一定需要是直接的父層,例如:我在fun1中定義的b,在fun3中的nonlocal也可以宣告修改b,當然fun2中也可以修改,並且如果是在fun3中修改b,那使用時就會以fun2中的b的執行結果為初始值(也就是初始值不在是fun1中b的值了);

nonlocal的作用就是在巢狀函式中使用,對上級函式的變數進行申明修改;