開發與維運

高階函數 | 手把手教你入門Python之四十五

本文來自於千鋒教育在阿里雲開發者社區學習中心上線課程《Python入門2020最新大課》,主講人姜偉。

高階函數

在Python中,函數其實也是⼀種數據類型。

def test():
 return 'hello world'
print(type(test)) # <class 'function'>

函數對應的數據類型是 function ,可以把它當做是⼀種複雜的數據類型。
既然同樣都是⼀種數據類型,我們就可以把它當做數字或者字符串來處理。

定義⼀個變量指向函數

在Python中,我們還可以定義⼀個變量,讓它來指向⼀個函數,相當於給函數起了⼀個別名。

def test():
 return 'hello wrold'
fun = test # 定義了⼀個變量fun,讓它指向了 test 這個函數
print(fun()) # 使⽤fun()可以直接調⽤test這個函數
print(id(fun)) # 1819677672040
print(id(test)) # 1819677672040

注意:在定義⼀個變量表示⼀個函數時,函數後⾯不能加括號!加括號表示的是調⽤這個函數。

def test():
 return 'hello world'
result = test() # 這種寫法是調⽤test函數,並把函數的返回值賦值給result變量
print(result()) # 這⾥會報錯 TypeError: 'str' object is not callable
fun = test # 這種寫法是給test函數起了⼀個別名,注意,這⾥的test後⾯不能加()
fun() # 可以使⽤別名調⽤這個函數

⾼階函數

既然變量可以指向函數,函數的參數能接收變量,那麼⼀個函數就可以接收另⼀個函數作為參數,同樣,我們還可以把⼀個函數當做另⼀個函數的返回值。這種函數的使⽤⽅式我們稱之為⾼階函數。

函數做為另⼀個函數的參數

def test(age,action):
 if age < 18:
 print('您還沒滿⼗⼋歲,請退出')
 action() # 把參數action直接當做⼀個函數來調⽤
def smoke():
 print('我已經年滿⼗⼋歲了,我想抽菸')

my_action = smoke # 定義⼀個變量my_action,讓它指向smoke函數
test(21, my_action) # 將my_action傳給 test 函數作為它的參數
test(21,smoke) # 還可以不再定義⼀個新的變量,直接傳⼊函數名

函數作為另⼀個函數的返回值

def test():
 print('我是test函數⾥輸⼊的內容')
def demo():
 print('我是demo⾥輸⼊的內容')
 return test # test 函數作為demo函數的返回值
result = demo() # 我是demo⾥輸⼊的內容 調⽤ demo 函數,把demo函數的返回值賦值給 result
print(type(result)) # <class 'function'> result 的類型是⼀個函數
result() # 我是demo⾥輸⼊的內容 我是test函數⾥輸⼊的內容 既然result是⼀個函數,那麼就可
以直接使⽤() 調⽤這個函數
demo()() # 我是demo⾥輸⼊的內容 我是test函數⾥輸⼊的內容

閉包

函數只是⼀段可執⾏代碼,編譯後就“固化”了,每個函數在內存中只有⼀份實例,得到函數的⼊⼝點便可以執⾏函數了。函數還可以嵌套定義,即在⼀個函數內部可以定義另⼀個函數,有了嵌套函數這種結構,便會產⽣閉包問題。

函數嵌套

在函數⾥⾯還可以定義函數,可以嵌套多層,執⾏需要被調⽤。

def outer():
 print('outer----hello')
 def inner(): # inner這個函數是在outer函數內部定義的
 print('inner----hello')
 inner() # inner函數只在outer函數內部可⻅
outer()
# inner() 這⾥會報錯,在outer函數外部⽆法訪問到inner函數

什麼是閉包

閉包是由函數及其相關的引⽤環境組合⽽成的實體(即:閉包=函數塊+引⽤環境)。

def outer(n):
 num = n
 def inner():
 return num+1
 return inner
print(outer(3)()) # 4
print(outer(5)()) # 5

在這段程序中,函數 inner 是函數 outer 的內嵌函數,並且 inner 函數是outer函數的返回值。我們注意到⼀個問題:內嵌函數 inner 中引⽤到外層函數中的局部變量num,Python解釋器會這麼處理這個問題呢? 先讓我們來看看這段代碼的運⾏結果,當我們調⽤分別由不同的參數調⽤ outer 函數得到的函數時,得到的結果是隔離的(相互不影響),也就是說每次調⽤outer函數後都將⽣成並保存⼀個新的局部變量num,這⾥outer函數返回的就是閉包。 如果在⼀個內部函數⾥,對在外部作⽤域(但不是在全局作⽤域)的變量進⾏引⽤,那麼內部函數就被認為是閉包(closure)。

修改外部變量的值

閉包⾥默認不能修改外部變量。

def outer(n):
 num = n
 def inner():
 num = num + 1
 return num
 return inner
print(outer(1)())

上述代碼運⾏時會報錯!

UnboundLocalError: local variable 'num' referenced before assignment

原因分析

在python⾥,只要看到了賦值語句,就會認為賦值語句的左邊是⼀個局部變量。 num = num + 1 這段代碼⾥, num 在 = 的左邊,python解析器會認為我們要修改 inner 函數⾥ num 這個局部變量,⽽這個變量使⽤之前是未聲明的,所以會報錯。

解決方案

我們分析過,報錯的原因在於當我們在閉包內修改外部變量時,會被python解析器誤會為內部函數的局部變量。所以,解決⽅案就在於,我們需要想辦法,讓解析器知道我們不是要修改局部變量,⽽是要修改外部變量。

  • 解決⽅法:使⽤ nonlocal 關鍵字
def outer(n):
 num = n
 def inner():
 nonlocal num # 修改前使⽤nonlocal關鍵字對 num 變量進⾏說明
 num = num + 1
 return num
 return inner
print(outer(2)())

Leave a Reply

Your email address will not be published. Required fields are marked *