阅读这篇文章我能学到什么?
说到函数的你可能会想到函数的参数、返回值、函数地址等,但是python3的函数用法非常灵活,比如允许函数嵌套定义,允许函数作为参数或变量传递,允许函数返回函数。这篇文章将为你讲解这些“灵活“的用法
——如果您觉得这是一篇不错的博文,希望你能给一个小小的赞,感谢您的支持。
在基础篇我们已经详细的讲解了函数的定义和调用,这里我们更深入的研究下。下面这个例子帮助你回忆函数的定义和调用。
代码示例:
def Function():
print("Call Function")
return 0
print(Function())
运行结果:
Call Function
0
函数调用按是否带()
可以分为两种方式,带括号时将会执行函数,括号内的实参会传递给形参,函数的返回值会给调用处。如果不带括号,则调用出得到的将会是函数的地址,函数并不会执行。
代码示例:
def Function():
print("Call Function")
return 0
#函数两种调用形式
print(Function())
print("---------------------------------")
print(Function)
运行结果:
Call Function
0
---------------------------------
另外,你可以尝试多运行几次,可以看到函数地址是变动的。
根据上面函数的两种调用形式,将函数赋值给变量相应也有两种可能,一种是变量拿到函数的返回值,一种是拿到函数地址。拿到返回值好理解,相当于拿到函数的执行结果,拿到地址有什么用?变量拿到函数地址后,相当于该变量也指向了这个函数,也即可以通过这个变量去调用这个函数。这其实是c/c++中指针的概念。
代码示例“
def Function():
print("Call Function")
return 0
Func1 = Function() #得到函数的返回值0
print(Func1)
print("--------------------------------------------------")
Func2 = Function #得到函数的地址
print(Func2)
print("--------------------------------------------------")
Func2() #通过变量去调用函数
运行结果:
Call Function
0
--------------------------------------------------
--------------------------------------------------
Call Function
函数不带()
传递给变量的是函数地址,变量拥有函数地址后加上()
就可调用函数执行。当然,变量存储的函数地址可以继续赋值给其他变量。更正确的理解是地址赋值给变量就是给对象(这里是函数)创建了一个新的引用,可以通过此引用去访问对象本身。(实际上python3一切传递的都是引用,后面补一张讲引用)。
我们知道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
被删除后就无法继续使用。总之,函数定义时函数名就是其第一个引用,我们可以为函数创建多个引用。函数的引用也可以被删除,即便它是第一个引用。
在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
函数时,实际上附带调用了Func2
和Func3
函数。
函数内部嵌套定义的函数不能被函数外部直接调用,但是我们前面提过函数可以有多个引用,其实函数时通过引用去调用的。我们可以通过全局的引用,让它直接指向内部嵌套定义的函数,就可以实现在函数外部去调用嵌套定义的函数了。
代码示例:
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 Func2
调用不带()
的函数名得到的函数的地址,如果将其作为函数的返回值,函数调用处将得到返回函数的地址。
代码示例:
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)
去调用函数并传递值1
,FuncA
里虽然定义了FuncB
和FuncC
函数,但是并没有被调用,所以他们还不会被执行。然后FuncA
调用结束并返回下一个函数FuncB
地址,函数地址
+()
就又构成函数调用,所以FuncA(1)(2)
到这里已经开始调用函数FuncB
并给其传值2
,同理FuncA(1)(2)(3)
会执行FuncC
也就不难理解了。执行的顺序是按嵌套顺序从外到内,参数列表()
也一一对应,能递推执行下去的基础是函数返回值返回了下一个执行函数的地址。
涉及到多层函数时,内层函数可以访问外层函数的参数。同名变量时,优先访问最内层变量。
代码示例:
def FuncA(NumA):
Str = "A"
def FuncB(NumB):
Str = "A"
def FuncC(NumC):
Str = "A" #各层函数之间同名变量,默认访问内层
print(NumA, NumB, NumC, Str) #内层函数访问外层函数的变量
return NumC #FuncC正常返回值
return FuncC #FuncB返回内层函数FuncC地址
return FuncB #FuncA返回内层函数FuncB地址
FuncA(1)(2)(3) #三个括号分别属于不同函数的参数列表
运行结果:
1 2 3 A
如果一个函数实现了一部分功能,另外一部分功能是暂时不确定的,我们可以将这部分不确定的功能单独封装成一个函数,作为参数传递进去供其调用。
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装饰器。