一文掌握Python內部函數:函數作為返回和閉包

Python 中,函數被認為是一等公民,這意味著它們可以像對待任何其他對象一樣對待。這種對一類函數的支持允許使用高階函數,這些函數可以接受其他函數作為參數或返回函數作為結果。這個強大的功能增強了 Python 編程的靈活性和表現力,允許更動態和功能化的編程模式。

為了理解為什麼這些能力是有用的,編寫一個返回其參數的恆等式函數。

def identity(number): 
  return number

identity(10)

#10

現在編寫另一個函數來求平方。

def square(number):
  return number**2

square(6)

#36

現在,將這兩種功能組合到一個函數中。為此,可以將這兩個函數編寫為另一個函數的內部函數。

Python 內部函數:

在 Python 中,內部函數是在另一個函數的主體中定義的函數。它也稱為嵌套函數。們的例子中:

def doWithNumber(number, squareIt = False):

  # identity function as inner function of doWithTheNumber
  def identity(): 
    return number

  # square function as inner function of doWithTheNumber
  def square():
    sq = number**2
    return sq

  # if else block to execute one of the function
  if squareIt == True:
    return square()
  else:
    return identity()

內部函數的作用域在外部函數內,這意味著它可以訪問外部函數的變數和參數。

在上面的例子中, identity()square() 被定義在 doWithNumber(number, squareIt= False) .內部函數可以訪問外部函數的變數和參數,例如,兩者都 i可以訪問 number ,但反之則不然, doWithNumber(number, squareIt= False) 無權訪問 哪個是 的內部變數 square() 。這稱為詞法範圍或閉包。

內部函數在各種場景中都很有用,例如:

  1. 封裝:內部函數允許我們將一個功能封裝在另一個函數的範圍內,將相關代碼保持在一起並與程序的其餘部分分開。上面的函數 doWithNumber(number, squareIt= False) 是封裝的一個很好的例子。
  2. 閉包:內部函數可以創建閉包,這些函數可以記住封閉範圍內的值,即使它們不再存在。這允許在函數調用之間保留狀態。
  3. 裝飾器:內部函數通常用於裝飾器中,這是一種通過用附加功能包裝函數或類來修改它們的行為的方法。內部函數用作包裝器,將所需的功能添加到原始函數中。

返回函數和閉包:

一個函數可以返回另一個函數。例如,可以稍微修改我們的 modifyNumber 數字函數以返回內部函數而不是執行它們。

def modifyNumber(squareIt = False):

  # identity function as inner function of doWithTheNumber
  def identity(number): 
    return number

  # square function as inner function of doWithTheNumber
  def square(number):
    sq = number**2
    return sq

  # if else block to execute one of the function
  if squareIt == True:
    return square
  else:
    return identity

現在,如果執行外部函數,這將為提供另一個函數。請記住,它不會執行任何函數,而是會提供另一個可執行函數。

justPrint = modifyNumber()

上面的代碼將返回我們內部函數 identity(number) 。現在我以用參數調用 justPrint 了。

justPrint(15)

#15

Or

squareNumber= modifyNumber(True)

squareNumber(25)

#625

這裡發生了什麼?

首先,當調用 squareNumber = modifyNumber(True) 它時,它會設置 squareIt = True .

然後它定義了我們的兩個函數 identity()square() .

然後它運行 if-else 塊,因為 squareIt = True ,該塊返回 square ;這是一個函數。

現在 squareNumber = square 相當於。

def squareNumber(number)
  sq = number**2
    return sq

因此,當調用 squareNumber(25) 它時,它返回了 625。

閉包是與內部函數和返回函數相關的重要概念。當嵌套/內部函數從其包含(封閉)函數的作用域引用變數時,即使封閉函數已完成執行,也會發生閉包。

比如之前的函數只能返回 identity 和 square,如果想添加額外的功能怎麼辦?修改一下函數。

def modifyNumber(power):
 def raisePower(number):
  return number ** power

 return raisePower

如果我們調用外部函數

identity = modifyNumber(1)

該函數將返回到另一個函數,即 raisePower .請記住,返回的是尚未執行的函數。為了執行它,必須調用它。

identity(10)
#10
identity(25)
#25

為什麼當調用 identity(10) 時返回 10。因已經看到設置 identity = modifyNumebr(1)power = 1 and 它的集合 identity = raisePower ,它等價於

def identity(number):
  return number ** power

但是raisePowe函數沒有名為 power 的變數。該變數 power 位於函數的作用域 modifyNumebr 內,該函數已完成執行。

現在調用 identity(10) 的時候,它將如何執行這一行代碼 return number ** power ,它如何獲取它的值 power = 1

在 Python 中,當外部函數完成執行並返回內部函數時,它會將內部函數與其封閉環境或閉包一起打包。這意味著函數對象保留其包含作用域中定義的所有變數和名稱的引用。這允許內部函數稍後訪問和使用這些變數,即使外部函數已完成其執行並且這些變數通常超出範圍。

這有點像父母給孩子留下遺產。即使父母可能已不復存在,孩子仍然可以訪問所有繼承的資產。

可以根據需要多次調用我們的外部函數。

squareNumber = modifyNumber(2)
cubeNumber =  modifyNumber(3)
rootNumber = modifyNumber(0.5)

可以隨心所欲地調用我們的內在功能。

squareNumber(10)
#100
squareNumber(5)
#25
cubeNumber(3)
#27
rootNumber(25)
#5.0
rootNumber(16)
#4.0

或者我們可以將這種內部功能與嵌套的俄羅斯娃娃進行比較。可以嵌套儘可能多的函數。

def eventPlanner(evenName):
  print(f'Welcome to {evenName}')
  listOfParticipant = []
 
  def register(name):
    listOfParticipant.append(name)
    print(listOfParticipant)
    
    listOfTask = []
    def addTask(task):
      listOfTask.append(task)
      print(listOfTask)
    
    return addTask
  return register

添加了三層功能,一個在另一個內部。這就像一個洋蔥,調用一個層會讓我們返回到另一個具有層的函數。

如果調用第一層,它將返回一個註冊函數。

fareRegistration = eventPlanner("1st School Fair")

#Welcome to 1st School Fair

現在可以調用第二層。它將列印已註冊人員的列表,並返回給我們一個任務功能。

enamTasks = fareRegistration("Enam")

# ['Enam']

似乎只有一名會員註冊了票價。讓再註冊一個。

kalamTasks = fareRegistration("Kalam")

#['Enam', 'Kalam']

這裡需要注意的一件事是,由於閉包,它為我們列印了兩個名稱,儘管外部函數完成了它的執行,但它提供了直接的內部函數,直到變數的最新狀態。

enamTasks("visit tea stall")
#['visit tea stall']

enamTasks("visit book stall")
#['visit tea stall', 'visit book stall']

enamTasks("attend Magic Show")
#['visit tea stall', 'visit book stall', 'attend Magic Show']

同樣,可以為同樣註冊了票價的 Kalam 先生添加任務。

kalamTasks("Perform Magic")
#['Perform Magic']

總之,這些是 Python 中強大的概念。例如,內部函數允許我們在另一個函數的主體中定義函數,從而提供了一種封裝相關功能並保持代碼模塊化和組織化的方法。內部函數可以訪問封閉函數的變數和參數,使它們能夠利用和操作其包含範圍內的數據。

從其他函數返回函數為我們的代碼增加了另一層靈活性。它允許我們創建高階函數,根據特定要求生成和自定義函數。這種能力使我們能夠編寫更通用和可重用的代碼,並促進函數式編程模式。

當內部函數保留對其封閉作用域的引用,捕獲必要的變數和名稱時,就會形成與返回函數密切相關的閉包。這種機制使內部函數能夠訪問和利用其包含環境的狀態,即使在封閉函數完成執行之後也是如此。閉包為函數提供持久性和上下文,允許它們保留特定於其創建點的數據和行為。

這些概念使我們能夠編寫模塊化、可重用和可定製的功能,從而增強代碼的可讀性、可維護性和效率。無論我們是將它們用於數據封裝、函數工廠、回調還是其他目的,理解和利用內部函數、返回函數和閉包都可以擴展我們的編程工具包,並使我們能夠為各種問題編寫優雅而有效的解決方案。