Python閉包詳解:理解閉包與可變類型和不可變類型的關系

一、定義

閉包(Closure) 指的是一個函數對象,即使其外部作用域的變量已經不存在了,仍然能訪問這些變量。簡單來說,閉包是由函數及其相關的環境變量組成的實體。

def outer():x = 10def inner():print(x)return innerf = outer()
f()  # 輸出10

對于這個示例,我們可以將inner函數稱為閉包,它滿足了嵌套函數,它滿足了使用外部函數outer的參數x,外部函數的返回值滿足了是inner函數本身

二、閉包

(1)閉包的形成條件

閉包必須是一個嵌套函數,并且內部函數引用了外的變量,外部函數的返回值是該內部函數,對于這個引用外部函數變量的內部函數稱為閉包。

  • 必須有嵌套函數(函數內部定義函數)
  • 內部函數引用了外部函數的變量
  • 外部函數返回內部函數對象

示例1:沒有嵌套函數,只有單層函數

def func():x = 10print(x)func()

示例2:嵌套函數存在,但內部函數沒有引用外部變量

def outer():x = 10def inner():print("Hello")return innerf = outer()
f()

示例3:嵌套函數引用了外部變量,但外部函數沒有返回該內部函數

def outer():x = 10def inner():print(x)inner()  # 調用內部函數,但沒有返回它outer()

(2)閉包的使用場景

1)數據隱藏和封裝:閉包可以把一些變量藏在函數里面,不讓外面直接訪問,這樣就不會亂用或誤改,避免弄亂全局變量,讓代碼更安全。

2)裝飾器實現:裝飾器本質上依賴閉包機制

3)函數工廠:閉包可以幫你根據不同需求,快速生成帶有特定設置或環境的函數,就像工廠按訂單生產不同產品一樣。

def make_multiplier(factor):# 這是一個函數工廠,傳入一個倍數 factordef multiplier(number):# multiplier 是閉包,記住了外面的 factorreturn number * factorreturn multiplier# 生成一個把數字乘以3的函數
times3 = make_multiplier(3)
print(times3(5))  # 輸出 15# 生成一個把數字乘以10的函數
times10 = make_multiplier(10)
print(times10(5))  # 輸出 50

(3)閉包的作用與優勢

1)減少全局變量使用,提升代碼安全性

閉包讓變量“藏”在函數里,避免把變量放到全局,減少沖突和錯誤,讓代碼更可靠。

2)保持函數運行環境,方便管理狀態

閉包可以記住外部變量的值,即使外部函數已經結束,內部函數還能繼續用這些數據,方便管理和維護程序狀態。

三、Python中的可變類型與不可變類型

3.1 變量類型

(1)不可變類型

包括:int、float、str、tuple、frozenset等,對象一旦創建,值不能被改變,任何修改都會生成新對象。

(2)可變類型

包括:list、dict、set、自定義類對象等,對象創建后,內容可以被修改,地址不變。

3.2 兩者區別與內存表現

1)不可變類型變量修改時,實際是創建了新的對象,變量指向新地址

a = 10
print(id(a))  # 假設輸出:140703079708016a = a + 1
print(id(a))  # 輸出:140703079708048 (地址發生變化)

說明:變量 a 原來指向值為10的對象,修改后指向了新創建的值為11的對象,地址發生變化。

2)可變類型變量修改時,變量指向的對象地址不變,內容發生變化

lst = [1, 2, 3]
print(id(lst))  # 假設輸出:140703080123456lst.append(4)
print(id(lst))  # 輸出:140703080123456 (地址未變)
print(lst)      # 輸出:[1, 2, 3, 4]

說明:變量 lst 指向的列表對象地址沒有變,但列表內部內容發生了變化。

3)通過id()函數可以觀察變量地址變化。id()?返回對象的內存地址標識,可以用來判斷變量是否指向同一個對象。

3.3 賦值和修改對變量的影響

1)對不可變類型變量賦值,變量綁定新對象,不影響原對象

2)對可變類型變量修改,直接改變對象內容,所有引用該對象的變量都能感知變化

四、閉包中變量的可變性影響

4.1 閉包對不可變類型變量的訪問與限制

閉包內部訪問外部不可變變量時,如果嘗試修改,會報錯(UnboundLocalError),因為修改會被當作局部變量賦值,導致訪問沖突。

def outer():x = 10  # 外部不可變變量def inner():x += 5  # 嘗試修改外部變量,報錯!print(x)inner()outer()

解釋:

在 inner 函數里,寫了 x += 5,這相當于想給 x 重新賦值。Python 看到這個,就把 x 當成是 inner 里的“新變量”,而不是外面那個已經有值的 x。但是這個“新變量”還沒被定義,結果你又想用它來計算,就出錯了。

簡單說,就是閉包里面如果你想修改外面那個數字,Python 會誤以為你是在用一個自己新建但還沒給值的變量,所以會報錯。要想修改外面的變量,需要告訴 Python “嘿,我用的是外面的那個變量”,這時候就得用 nonlocal。

4.2 使用 nonlocal 關鍵字修改不可變變量

nonlocal 的作用就是告訴 Python:“我想用的是外面函數里的那個變量,不是新建一個新的。”

所以在 inner 里寫了 nonlocal x,Python 就知道你要改的是外面 outer 函數里的 x,然后你給它加 5,修改成功了。這樣,inner 里和外面打印的 x 都變成了 15,因為它們指的是同一個變量。

def outer():x = 10def inner():nonlocal xx += 5print(x)inner()print(x)outer()
"""
輸出:
15
15
"""
4.3 閉包中對可變類型變量的修改與訪問

對可變類型變量,閉包內部可以直接修改其內容,無需nonlocal。當然建議使用nonlocal關鍵字聲明。

def outer():lst = [1, 2, 3]def inner():lst.append(4)print(lst)inner()print(lst)outer()
"""
輸出
[1, 2, 3, 4]
[1, 2, 3, 4]"""

為什么修改lst不需要使用nonlocal關鍵字呢。由于lst是一個列表他是一個可變類型,內層函數在對lst進行添加操作時,并沒有改變lst本身,它所指向的內存空間也沒有發生改變。如果內層函數代碼為del lst 想要刪除這個列表,也就是對外層函數的lst做了修改操作,此時就會發生報錯UnboundLocalError,如果加上關鍵字nonlocal進行聲明,就可以對它進行刪除。

4.4 常見誤區

1)不是所有閉包里的變量修改都要用 nonlocal

def outer():lst = [1, 2, 3]  # 可變類型變量def inner():lst.append(4)  # 直接修改列表內容,無需nonlocalprint(lst)inner()print(lst)outer()

2)別把“換變量”和“改內容”搞混了

def outer():x = 10  # 不可變類型lst = [1, 2, 3]  # 可變類型def inner():# x += 1  # 重新綁定,會報錯,需要nonlocal# 改成下面這樣才合法:nonlocal xx += 1# 對列表內容修改,不是重新綁定lst.append(4)print(x, lst)inner()print(x, lst)outer()

3)別忽略了可變對象改了內容,閉包里的變量也跟著變

def outer():d = {'count': 0}  # 可變字典def inner():d['count'] += 1  # 修改字典內容print(d['count'])return innerf = outer()
f()  # 輸出1
f()  # 輸出2

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

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

相關文章

BotCash:GPT-5發布觀察 工程優化的進步,還是技術突破的瓶頸?

BotCash:GPT-5發布觀察 工程優化的進步,還是技術突破的瓶頸? 在GPT-4以多模態能力震撼業界的一年后,GPT-5的亮相顯得有些“平靜”。當人們期待著又一場顛覆性技術革命時,這場發布會更像是給大模型技術按下了“精細打磨…

AJAX學習(2)

目錄 一.XMLHttpRequest 二.XMLHttpRequest——查詢參數 三.案例——地區查詢 四.XMLHttpRequest_數據提交 五.Promise 六.Promise三種狀態 七.PromiseeeXHR獲取省份列表(案例) 八.封裝-簡易axios-獲取省份列表 九.封裝-簡易axios-獲取地區列表 …

解決 pip 安裝包時出現的 ReadTimeoutError 方法 1: 臨時使用鏡像源(單次安裝)

解決 pip 安裝包時出現的 ReadTimeoutError 當您在使用 pip 安裝 Python 包時遇到 pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(hostfiles.pythonhosted.org, port443): Read timed out. 錯誤時,這通常是由于網絡問題導致的連接超時。P…

Linux下使用Samba 客戶端訪問 Samba 服務器的配置(Ubuntu Debian)

在 Linux 系統中,Samba 提供了與 Windows 系統文件共享的便利方式。本文將詳細介紹在 Ubuntu 和 Debian 系統下如何安裝 Samba 客戶端、訪問共享資源,并實現遠程目錄掛載和開機自動掛載。 文章參考自(感謝分享):https…

解決dedecms文章默認關鍵字太短的問題

在管理文章或軟件的時候,大家在添加關鍵字和內容摘要的時候,是不是對這樣的情況感到比較的郁悶,我的關鍵字設定的明明非常的好,可是添加或修改后,會被無緣無故的截去很多,想必大家也都非常的明白&#xff0…

K8s-kubernetes(二)資源限制-詳細介紹

K8s如何合理規定對象資源使用 基本概念 Kubernetes中,占用資源的最小單元為單個PodKubernetes中,資源占用主要針對服務器的CPU、內存 為什么要做資源限制 對于Kubernetes集群而言,所有Pod都會占用K8s集群所在服務器的資源,如果不做…

量子神經網絡:從NISQ困境到邏輯比特革命的破局之路

——解析2025千比特時代開發者的機遇與行動框架 引言:量子計算的“20比特魔咒”與千比特悖論 當開發者被建議“避免在>20量子比特電路訓練”時,富士通卻宣布2025年實現10,000物理比特系統。這一矛盾揭示了量子計算從NISQ時代向FTQC時代躍遷的核心邏輯:千比特突破非為直接…

react+vite-plugin-react-router-generator自動化生成路由

前言:react項目實際使用中有很多提升性能與功能的插件,今天來說一說vite里面提供的vite-plugin-react-router-generator,他主要提供了自動生成路由的功能,配合我們的loadable/component可以實現路由的懶加載與統一管理。1、實現效…

服務器查看 GPU 占用情況的方法

在 Linux 系統中查看 GPU 占用情況,主要取決于你的 GPU 類型(NVIDIA/AMD),以下是常用方法: 一、NVIDIA GPU(最常用,如 RTX 系列、Tesla 系列) 使用 NVIDIA 官方工具 nvidia-smi&…

【Docker實戰進階】Docker 實戰命令大全

Docker 實戰命令大全 Docker 實戰場景,以 Nginx 為核心示例,梳理容器生命周期、鏡像管理、網絡配置、數據持久化及 Compose 編排的核心命令與最佳實踐。 一、容器生命周期管理 1. 基礎生命周期命令 docker run - 創建并啟動容器 核心功能:基于…

PyCharm 2025.2:面向工程師的 AI 工具

引言 隨著人工智能技術的快速發展,AI 工程師對開發工具的需求也在不斷提升。PyCharm 2025.2 版本帶來了革命性的 AI 工具包,將 AI 開發所需的實驗、調試、評估和部署功能原生集成到 IDE 中。這一重大更新不僅提升了開發效率,也為 AI 工程師提…

爬蟲逆向--Day15--核心逆向案例2(Python逆向實現請求加密、請求堆棧、攔截器關鍵字)

一、逆向案例之Python逆向實現請求加密//具體代碼如下 function l(t, e) {return t.toString().toUpperCase() > e.toString().toUpperCase() ? 1 : t.toString().toUpperCase() e.toString().toUpperCase() ? 0 : -1}function u(t) {for (var e Object.keys(t).sort(l)…

時序數據庫市場前景分析

1. 引言隨著物聯網(IoT)、工業互聯網、金融科技、智慧城市等領域的快速發展,數據呈現爆發式增長,其中時間序列數據(Time-Series Data)占據了重要地位。時序數據庫(Time-Series Database, TSDB&a…

【網絡安全測試】Burp Suite使用指導、配置及常見問題介紹(有關必回)

Burp Suite 是**滲透測試領域事實上的標準工具**,尤其擅長Web應用與API安全測試。針對AI系統,它主要用于測試模型API、管理后臺等Web接口。以下是專業級使用指南:---### **一、 核心模塊與功能概覽**| **模塊** | **核心功能** | **AI測試重點…

iOS 26 一鍵登錄失效:三大運營商 SDK 無法正常獲取手機號

近期,不少開發者和用戶反饋,在升級到 iOS 26 系統后,App 內的 一鍵登錄功能無法正常使用。無論是移動、電信還是聯通的 SDK,都會出現無法獲取手機號的情況,導致用戶需要改用短信驗證碼或手動輸入手機號完成登錄。問題現…

OpenLayers與Vue.js結合實現前端地圖應用

OpenLayers與Vue.js結合實現前端地圖應用 下面我將為您展示如何將OpenLayers與Vue.js結合創建一個功能豐富的前端地圖應用。這個教程包含了基礎地圖展示、標記點、地圖控件以及交互功能。 實現結果 實現思路 在Vue項目中集成OpenLayers庫創建基礎地圖視圖和OSM圖層添加標記點…

VisDrone數據集,專為無人機視覺任務打造

在農業巡查、環保監測、安防布控等廣闊天地,無人機(UAV)早已超越了“拍照打卡”的酷炫標簽,成為不可或缺的智能之眼。然而,當計算機視覺模型從地面“抬頭”望向無人機視角時,迎接它的卻是截然不同的挑戰&am…

【Python】Python 函數基本介紹(詳細版)?

Python 函數基本介紹(詳細版)? 文章目錄Python 函數基本介紹(詳細版)?前言一、函數的創建?1.1 函數名的命名規則?1.2 函數的創建?1.3 函數的調用?二、函數的參數?2.1 形參和實參?2.2 位置參數?2.3 關鍵字參數?2.4 默認參…

【前端Vue】log-viewer組件的使用技巧

目錄 修改行號和組件的樣式 修改高亮顯示的內容和顏色 **log-viewer組件合集** 【前端Vue】如何優雅地展示帶行號的日志文件或文本內容(log-viewer組件的使用) 【前端Vue】使用log-viewer組件時的踩坑記錄 【前端Vue】log-viewer組件的使用技巧 【前…

OpenCV Python——報錯AttributeError: module ‘cv2‘ has no attribute ‘bgsegm‘,解決辦法

Python在使用 bgsubmog cv2.bgsegm.createBackgroundSubtractorMOG() 去除背景,報錯AttributeError: module ‘cv2‘ has no attribute ‘bgsegm‘ 報錯原因:使用的python環境中沒有安裝擴展包contrib 可以通過pip或者conda安裝 pip install opencv-con…