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装饰器。

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