首頁 >後端開發 >Python教學 >Python中關於閉包的使用實例

Python中關於閉包的使用實例

黄舟
黄舟原創
2017-10-02 19:43:431050瀏覽

這篇文章主要介紹了Python 基礎教程之閉包的使用方法的相關資料,希望大家透過本文能幫助到大家,需要的朋友可以參考下

Python 基礎教程之閉包的使用方法

前言:

閉包(closure)是函數式程式設計的重要的語法結構。函數式程式設計是一種程式設計範式 (而過程導向程式設計和物件導向程式設計也都是程式設計範式)。在過程導向程式設計中,我們見過函數(function);在物件導向程式設計中,我們看過物件(object)。函數和物件的根本目的是以某種邏輯方式組織程式碼,並提高程式碼的可重複使用性(reusability)。閉包也是一種組織程式碼的結構,它同樣提高了程式碼的可重複使用性。

不同的語言實作閉包的方式不同。 Python以函數物件為基礎,為閉包這個語法結構提供支援的 (我們在特殊方法與多範式中,已經多次看到Python使用物件來實作一些特殊的語法)。 Python一切皆對象,函數這個語法結構也是一個對象。在函數對像中,我們像使用普通對像一樣使用函數對象,例如更改函數對象的名字,或將函數對像作為參數進行傳遞。

 函數物件的作用域

和其他物件一樣,函數物件也有其存活的範圍,也就是函數物件的作用域。函數物件是使用def語句定義的,函數物件的作用域與def所在的層級相同。例如下面程式碼,我們在line_conf函數的隸屬範圍內定義的函數line,就只能在line_conf的隸屬範圍內呼叫。


def line_conf():
  def line(x):
    return 2*x+1
  print(line(5))  # within the scope


line_conf()
print(line(5))    # out of the scope

line函數定義了一條直線(y = 2x + 1)。可以看到,在line_conf()中可以呼叫line函數,而在作用域之外呼叫line將會有下面的錯誤:


##

NameError: name 'line' is not defined

說明這時已經在作用域之外。

同樣,如果使用lambda定義函數,那麼函數物件的作用域與lambda所在的層級相同。

 閉包

函數是一個對象,所以可以作為某個函數的回傳結果。


def line_conf():
  def line(x):
    return 2*x+1
  return line    # return a function object

my_line = line_conf()
print(my_line(5))

上面的程式碼可以成功運行。 line_conf的回傳結果被賦給line物件。上面的程式碼將會列印11。

如果line()的定義中引用了外部的變量,會發生什麼事?


def line_conf():
  b = 15
  def line(x):
    return 2*x+b
  return line    # return a function object

b = 5
my_line = line_conf()
print(my_line(5))

我們可以看到,line定義的隸屬程式區塊中引用了高層的變數b,但b資訊存在於line的定義之外(b的定義並不在line的隸屬程序區塊)。我們稱b為line的環境變數。事實上,line作為line_conf的回傳值時,line中已經包含b的取值(儘管b並不隸屬於line)。

上面的程式碼會印25,也就是說,line所參考的b值是

函數物件定義時可供參考的b值,而不是使用時的b值。

一個函數和它的環境變數合在一起,就構成了一個

閉包(closure)。 在Python中,所謂的閉包是一個包含有環境變數取值的函數物件。環境變數取值被保存在函數物件的__closure__屬性中。例如下面的程式碼:


def line_conf():
  b = 15
  def line(x):
    return 2*x+b
  return line    # return a function object

b = 5
my_line = line_conf()
print(my_line.__closure__)
print(my_line.__closure__[0].cell_contents)

__closure__裡包含了一個元組(tuple)。這個元組中的每個元素是cell類型的物件。我們看到第一個cell包含的就是整數15,也就是我們建立閉包時的環境變數b的取值。

下面來看一個閉包的實際例子:


def line_conf(a, b):
  def line(x):
    return ax + b
  return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))

這個例子中,函數line與環境變數a,b構成閉包。在建立閉包的時候,我們透過line_conf的參數a,b說明了這兩個環境變數的取值,這樣,我們就確定了函數的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數a,b,就可以得到不同的直線表達函數。由此,我們可以看到,閉包也具有提高程式碼可重複使用性的作用。

如果沒有閉包,我們需要每次建立直線函數的時候同時說明a,b,x。這樣,我們就需要更多的參數傳遞,也減少了程式碼的可移植性。利用閉包,我們實際上創建了泛函。 line函數定義一種廣泛意義的函數。這個函數的一些方面已經確定(必須是直線),但另一些方面(例如a和b參數待定)。隨後,我們根據line_conf傳遞來的參數,透過閉包的形式,將最終函數確定下來。

閉包與平行運算

閉包有效的減少了函數所需定義的參數數目。這對於並行運算來說有重要的意義。在並行運算的環境下,我們可以讓每台電腦負責一個函數,然後將一台電腦的輸出和下一台電腦的輸入串連起來。最終,我們像流水線一樣工作,從串聯的電腦集群一端輸入數據,從另一端輸出數據。這樣的情境最適合只有一個參數輸入的函數。閉包就可以實現這一目的。

並行運算正稱為一個熱點。這也是函數式程式設計又熱起來的重要原因。函數式程式設計早在1950年代就已經存在,但應用並不廣泛。然而,我們上面描述的流水線式的工作並行集群過程,正適合函數式程式設計。由於函數式程式設計這一天然優勢,越來越多的語言也開始加入對函數式程式設計範式的支援。

以上是Python中關於閉包的使用實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn