python3進階篇(一)——函數的深入理解

python3進階篇(一)——函數的深入理解

閱讀這篇文章我能學到什麼?
  說到函數的你可能會想到函數的參數、返回值、函數地址等,但是python3的函數用法非常靈活,比如允許函數嵌套定義,允許函數作爲參數或變量傳遞,允許函數返回函數。這篇文章將爲你講解這些“靈活“的用法

——如果您覺得這是一篇不錯的博文,希望你能給一個小小的贊,感謝您的支持。

1 再看函數

  在基礎篇我們已經詳細的講解了函數的定義和調用,這裏我們更深入的研究下。下面這個例子幫助你回憶函數的定義和調用。
代碼示例:

def Function():
    print("Call Function")
    return 0

print(Function())

運行結果:

Call Function
0

1.1 函數調用的兩種形式

  函數調用按是否帶()可以分爲兩種方式,帶括號時將會執行函數,括號內的實參會傳遞給形參,函數的返回值會給調用處。如果不帶括號,則調用出得到的將會是函數的地址,函數並不會執行。
代碼示例:

def Function():
    print("Call Function")
    return 0

#函數兩種調用形式
print(Function())
print("---------------------------------")
print(Function)

運行結果:

Call Function
0
---------------------------------
<function Function at 0x000002B5F0896280>

  另外,你可以嘗試多運行幾次,可以看到函數地址是變動的。

2 函數賦值給變量,嘗試刪除函數

2.1 將函數賦值給變量

  根據上面函數的兩種調用形式,將函數賦值給變量相應也有兩種可能,一種是變量拿到函數的返回值,一種是拿到函數地址。拿到返回值好理解,相當於拿到函數的執行結果,拿到地址有什麼用?變量拿到函數地址後,相當於該變量也指向了這個函數,也即可以通過這個變量去調用這個函數。這其實是c/c++中指針的概念。
代碼示例“

def Function():
    print("Call Function")
    return 0

Func1 = Function()                  #得到函數的返回值0
print(Func1)
print("--------------------------------------------------")
Func2 = Function                    #得到函數的地址
print(Func2)

print("--------------------------------------------------")
Func2()                             #通過變量去調用函數

運行結果:

Call Function
0
--------------------------------------------------
<function Function at 0x0000018682F66280>
--------------------------------------------------
Call Function

  函數不帶()傳遞給變量的是函數地址,變量擁有函數地址後加上()就可調用函數執行。當然,變量存儲的函數地址可以繼續賦值給其他變量。更正確的理解是地址賦值給變量就是給對象(這裏是函數)創建了一個新的引用,可以通過此引用去訪問對象本身。(實際上python3一切傳遞的都是引用,後面補一張講引用)。

2.2 嘗試del函數

  我們知道python3中有關鍵字del可以將各類對象的引用刪除,注意刪除的是引用,而不是直接刪除對象本身,當一個對象沒有被任何引用指向時,垃圾回收機制會自動將其釋放。函數定義時的函數名也只是該函數的一個引用,同樣可以把這個引用刪除。del函數實際是刪除了該函數的第一個引用。

def Function():
    print("Call Function")
    return 0

Func = Function                     #得到函數的地址
del Function

# Function()                        #error: NameError: name 'Function' is not defined
Func()                              #通過變量去調用函數

運行結果:

Call Function

  函數名Function是該函數創建時存在的第一個函數引用,通過傳遞可以給這個函數創建新的引用Func,這時如果刪除引用Function那麼引用Func依舊有效,但是Function被刪除後就無法繼續使用。總之,函數定義時函數名就是其第一個引用,我們可以爲函數創建多個引用。函數的引用也可以被刪除,即便它是第一個引用。

3 函數嵌套定義

3.1 嘗試嵌套定義函數

  在python3中允許函數的嵌套定義,比如在函數Func1裏可以定義函數Func2,在函數Func2裏可以定義函數Func3。定義函數時爲了調用,但是要注意函數嵌套時的作用域。如果函數Func2在函數Func1裏被被定義,那麼只能在Func1函數裏調用Func2函數,外部不能直接調用(與全局函數最大的不同就是作用域)。
運行結果:

def Func1():
    print("Call Func1")

    def Func2():
        print("Call Func2")

        def Func3():
            print("Call Func3")

        Func3()

    Func2()
    #Func3()                         #error: NameError: name 'Func2' is not defined

Func1()                              #調用Func1,也就調用了Func2和Func3
#Func2()                             #error: NameError: name 'Func2' is not defined
#Func3()                             #error: NameError: name 'Func2' is not defined

代碼示例:

Call Func1
Call Func2
Call Func3

  因爲Func2函數內調用了Func3函數,Func1函數內調用了Func2函數,所以當我們調用了Func1函數時,實際上附帶調用了Func2Func3函數。

3.2 通過引用調用嵌套定義的函數

  函數內部嵌套定義的函數不能被函數外部直接調用,但是我們前面提過函數可以有多個引用,其實函數時通過引用去調用的。我們可以通過全局的引用,讓它直接指向內部嵌套定義的函數,就可以實現在函數外部去調用嵌套定義的函數了。
代碼示例:

Func = None

def Func1():
    global Func                     #聲明全局變量
    print("Call Func1")

    def Func2():
        print("Call Func2")

    Func = Func2
    Func2()

Func1()                             #調用Func1,也就調用了Func2和Func3
print("---------------------------------------------")
Func()                               #通過全局引用去調用內部嵌套定義的函數

運行結果:

Call Func1
Call Func2
---------------------------------------------
Call Fun2

4 函數返回函數

  調用不帶()的函數名得到的函數的地址,如果將其作爲函數的返回值,函數調用處將得到返回函數的地址。
代碼示例:

def Func1():
    print("Call Func1")

    def Func2():
        print("Call Func2")

    return Func2

Func = Func1()                            #調用Func1,也就調用了Func2和Func3
print("------------------------------------")
Func()                                    #外部訪問函數內部定義的嵌套函數

運行結果:

Call Func1
------------------------------------
Call Func2

  用過函數返回內部函數的地址,也能實現外部訪問內部函數。
  函數嵌套定義和函數返回函數結合,在函數調用是會有“多括號”情況的產生,非常有意思。
代碼示例:

def FuncA(NumA):
    print("Call FuncA", "Param:%d" % NumA)

    def FuncB(NumB):
        print("Call FuncB", "Param:%d" % NumB)

        def FuncC(NumC):
            print("Call FuncC", "Param:%d" % NumC)

            return NumC                                 #FuncC正常返回值
        return  FuncC                                   #FuncB返回內層函數FuncC地址
    return  FuncB                                       #FuncA返回內層函數FuncB地址

print(FuncA(1)(2)(3))                                   #三個括號分別屬於不同函數的參數列表
print("---------------------------------------")
print(((FuncA(1))(2))(3))                               #加上括號表明其搭配順序

運行結果:

Call FuncA Param:1
Call FuncB Param:2
Call FuncC Param:3
3
---------------------------------------
Call FuncA Param:1
Call FuncB Param:2
Call FuncC Param:3
3

  邏輯並不算複雜,我們來理一理。我們使用FuncA(1)去調用函數並傳遞值1FuncA裏雖然定義了FuncBFuncC函數,但是並沒有被調用,所以他們還不會被執行。然後FuncA調用結束並返回下一個函數FuncB地址,函數地址+()就又構成函數調用,所以FuncA(1)(2)到這裏已經開始調用函數FuncB並給其傳值2,同理FuncA(1)(2)(3)會執行FuncC也就不難理解了。執行的順序是按嵌套順序從外到內,參數列表()也一一對應,能遞推執行下去的基礎是函數返回值返回了下一個執行函數的地址。

5 函數作爲參數傳遞

  如果一個函數實現了一部分功能,另外一部分功能是暫時不確定的,我們可以將這部分不確定的功能單獨封裝成一個函數,作爲參數傳遞進去供其調用。

def Function(Func):
    print("Call Function")
    Func()

def Func1():
    print("Call Func1")

def Func2():
    print("Call Func2")

Function(Func1)                                      #將Func1函數的地址傳遞給Function函數
print("---------------------------")
Function(Func2)                                      #將Func2函數的地址傳遞給Function函數

運行結果:

Call Function
Call Func1
---------------------------
Call Function
Call Func2

  這裏的Function函數功能有兩個:1、實現自己函數內的邏輯。2、調用外部傳入的函數,但外部函數執行的邏輯Function是不關心的,由外部函數自己決定。python3裏有 裝飾器 的概念,其實就是函數作爲參數用法的演化,我將會在後面章節介紹python裝飾器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章