Python深入分享之閉包

閉包(closure)是函數式編程的重要的語法結構。函數式編程是一種編程范式 (而面向過程編程和面向對象編程也都是編程范式)。在面向過程編程中,我們見到過函數(function);在面向對象編程中,我們見過對象(object)。函數和對象的根本目的是以某種邏輯方式組織代碼,并提高代碼的可重復使用性(reusability)。閉包也是一種組織代碼的結構,它同樣提高了代碼的可重復使用性。

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

函數對象的作用域
和其他對象一樣,函數對象也有其存活的范圍,也就是函數對象的作用域。函數對象是使用def語句定義的,函數對象的作用域與def所在的層級相同。比如下面代碼,我們在line_conf函數的隸屬范圍內定義的函數line,就只能在line_conf的隸屬范圍內調用。

def line_conf():def line(x):return 2*x+1print(line(5))   # within the scopeline_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+1return line       # return a function objectmy_line = line_conf()
print(my_line(5))  

上面的代碼可以成功運行。line_conf的返回結果被賦給line對象。上面的代碼將打印11。

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

def line_conf():b = 15def line(x):return 2*x+breturn line       # return a function objectb = 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 = 15def line(x):return 2*x+breturn line       # return a function objectb = 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 a*x + breturn lineline1 = 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年代就已經存在,但應用并不廣泛。然而,我們上面描述的流水線式的工作并行集群過程,正適合函數式編程。由于函數式編程這一天然優勢,越來越多的語言也開始加入對函數式編程范式的支持。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/161937.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161937.shtml
英文地址,請注明出處:http://en.pswp.cn/news/161937.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

substr()與substring()的區別

在 JavaScript 編程語言中,substr() 和 substring() 都是字符串函數,用于截取指定位置的子字符串。雖然這兩個函數都可以用于截取字符串,但它們之間存在一些區別。 substr() 語法:string.substr(start,length) 參數值&#xff1a…

IDEA中 java: 警告: 源發行版 11 需要目標發行版 11 如何解決

步驟1找到項目結構,下面有兩種方式 步驟2找到 模塊中對應的項目,修改對應的源的語言級別和依賴的模塊SDK(M) 步驟3,啟動一下,看有無問題, 步驟4,去文件-->設置-->構建、執行、部署-->編譯器-->…

中職組網絡安全B模塊-滲透提權2

任務五:滲透提權2 任務環境說明: 僅能獲取xxx的IP地址 用戶名:test,密碼:123456 訪問服務器主機,找到主機中管理員名稱,將管理員名稱作為Flag值提交; Flag:doyoudoyoudo 訪問服…

C語言二十三彈---求第N項斐波那契數列的值

C語言求第N項斐波那契數列的值 定義:斐波那契數列指的是這樣一個數列:1,1,2,3,5,8,13,21,34,55,89…自然中的斐波那契數列&#xff0…

感恩三十載 再創新輝煌——中國音樂著作權協會成立30周年暨著作權集體管理制度實施30周年紀念大會在京召開

感恩三十載 再創新輝煌 2023年11月19日,中國音樂著作權協會成立30周年暨著作權集體管理制度實施30周年紀念大會在北京舉行。中宣部副部長張建春,國際作者和作曲者協會聯合會(CISAC)總干事甘迪奧龍(Gadi Oron&#xff0…

深入了解批處理文件:從基礎到實例

1. 什么是批處理文件? 批處理文件是一種包含一系列命令的文本文件,通常用于自動化執行一系列任務。在不同操作系統中,批處理也有不同的名稱,如在Windows中被稱為批處理文件(.bat),而在Linux中則…

【MISRA C 2012】Rule 5.1 外部標識符應該是不同的

1. 規則1.1 原文1.2 分類 2. 關鍵描述3. 代碼實例 1. 規則 1.1 原文 Rule 5.1 External identifiers shall be distinct Category Required Analysis Decidable, System Applies to C90, C99 1.2 分類 規則5.1:外部標識符應該是不同的 Required必須類規范。 2.…

1、基礎入門——操作系統文件下載反彈SHELL防火墻繞過

名詞解釋 POC:驗證漏洞存在的代碼; EXP:利用漏洞的代碼; payload:漏洞利用載荷, shellcode:漏洞代碼, webshell:特指網站后門; 木馬:強調控制…

【STL】set, map, multiset, multimap的介紹及使用

TOC 目錄 關聯式容器 樹形結構與哈希結構 鍵值對 set set的定義方式 set的使用 multiset map map的介紹 map的定義方式 map的插入 insert函數的參數 insert函數的返回值 map的查找 map的刪除 map的[ ]運算符重載 map的迭代器遍歷 map的其他成員函數 multim…

Day40:139.單詞拆分、背包問題總結

文章目錄 139.單詞拆分思路代碼實現 背包問題總結背包類型遞推公式 139.單詞拆分 題目鏈接 思路 確定dp數組以及下標的含義 dp[i] : 從0開始長度為i的字符串是否可以拆分為一個或多個在字典中出現的單詞確定遞推公式 如果確定dp[j] 是true,且 [j, i] 這個區間的子…

【bug】uniapp的image組件渲染gif圖,只有第一次點擊的時候有動效,需要每次點擊都有gif效果,已解決

前兩天遇到的問題,暫時沒有解決,就擱置了。 不解決又難受,還好今天解決了,記錄下 需求: 兩個gif圖,分別代表點擊之后的男生和女生,并且有兩個靜態的男生和女生圖片 當男生靜態圖被點擊的時候切…

關于ElectronVue3中集成訊飛星火AI

前言:我的最終目的是為了在QQ上集成一個AI機器人,因此在這里先實現一個簡單的集成 先上效果圖 總體還是很簡單的,我在調用websock獲取回復內容的基礎上另外集成了一個事件總線,讓我們在調用獲取消息的時候能夠更加方便快捷 工具代…

聯想拯救者Lenovo Legion R9000K 2021H(82N6)原裝出廠Windows10/Win11系統ISO鏡像

鏈接:https://pan.baidu.com/s/13NkeCXNdV0Ib5eeRnZUeAQ?pwdnlr7 提取碼:nlr7 拯救者筆記本電腦原廠WIN系統自帶所有驅動、出廠主題壁紙、系統屬性專屬LOGO標志、Office辦公軟件、聯想電腦管家等預裝程序 所需要工具:16G或以上的U盤 文…

啟發式搜索算法-人工智能

第1關:評估函數和啟發信息 第2關:A*搜索算法 class Array2D:"""說明:1.構造方法需要兩個參數,即二維數組的 寬和高2.成員變量w和h是二維數組的寬和高3.使用:‘對象[x][y]’可以直接取到相應的值4.數組的默認值都是0"""def __init__(s…

使用PySpark 結合Apache SystemDS 進行信號處理分析 (離散傅立葉變換)的簡單例子

文章大綱 簡介 :什么是 SystemDS ?環境搭建與數據 準備數據預處理模型訓練 與 結果評估參考文獻簡介 :什么是 SystemDS ? SystemDS is an open source ML system for the end-to-end data science lifecycle from data integration, cleaning, and feature engineering, ov…

干貨分享丨客戶旅程管理的框架與案例

融合煥新,數字化轉型打造客戶經營新旅程。本文圍繞該主題詳細描述了客戶旅程管理的框架,并通過實踐案例進一步驗證客戶旅程管理的價值。 以下內容根據行業知名企業專家劉勝強的分享整理,完整版內容請點擊文末“閱讀原文”觀看哦~ 一、客戶時代…

【libGDX】使用Mesh繪制矩形

1 前言 使用Mesh繪制三角形 中介紹了繪制三角形的方法,本文將介紹繪制正方形的方法。 libGDX 以點、線段、三角形為圖元,沒有提供繪制矩形內部的接口。要繪制矩形內部,必須通過三角形拼接而成,如下圖,是通過GL_TRIANGL…

基于JavaWeb+SSM+Vue家庭記賬本微信小程序系統的設計和實現

基于JavaWebSSMVue家庭記賬本微信小程序系統的設計和實現 源碼獲取入口前言主要技術系統設計功能截圖Lun文目錄訂閱經典源碼專欄Java項目精品實戰案例《500套》 源碼獲取 源碼獲取入口 前言 1.1選題背景 互聯網是人類的基本需求,特別是在現代社會,個人…

看不慣AI版權作品被白嫖!Stability AI副總裁選擇了辭職,曾領導開發Stable Audio

近日,OpenAI的各種大瓜真是讓人吃麻了。 而就在Sam Altmam被開除前兩天,可能沒太多人注意到Stability AI副總裁Newton—Rex因看不慣StabilityAI在版權保護上的行為選擇辭職一事。 大模型研究測試傳送門 GPT-4傳送門(免墻,可直接…

SPASS-聚類和判別分析

聚類與判別分析概述 基本概念 聚類分析 聚類分析的基本思想是找出一些能夠度量樣本或指標之間相似程度的統計量,以這些統計量為劃分類型的依據,把一些相似程度較大的樣本(或指標)聚合為一類,把另外一些彼此之間相似程…