python中的內(nèi)置globals()函數(shù)實(shí)例詳解
最近在看一段代碼時,發(fā)現(xiàn)作者使用globals()來應(yīng)對無法確定變量名的情況,故此對該函數(shù)進(jìn)行了詳細(xì)的了解。
一、基本情況
globals() 是Python的標(biāo)準(zhǔn)內(nèi)置函數(shù),用于返回當(dāng)前模塊中定義的全局變量的字典(包含所有的全局變量、函數(shù)、類、導(dǎo)入的模塊),其中鍵是變量名,值是變量的值。字典可進(jìn)行修改,修改后會隨之反映到全局變量上。對于函數(shù)內(nèi)的代碼,返回的為在定義函數(shù)時確定的該字典,無論在哪里調(diào)用都保持不變。
globals是特殊場景的解決方案,如動態(tài)批量處理全局變量/函數(shù)(eg:配置加載)、調(diào)試時快速排查全局狀態(tài)問題、框架/庫的底層實(shí)現(xiàn),且有完善的封裝和文檔。
缺點(diǎn):可讀性差,命名沖突、作用域誤區(qū)、線程不安全、性能損耗、破壞封裝
重點(diǎn):非必要不使用,優(yōu)先使用字典/類進(jìn)行替代,使用時做好校驗(yàn)、鎖和注釋。
二、常用場景實(shí)例
核心價值是動態(tài)操作全局命名空間,由于操作的是全局狀態(tài),使用時需要謹(jǐn)慎。
1、批量處理配置變量、根據(jù)配置變量名讀取對應(yīng)的值
config0= 10
config1 = 30
config2 = "INFO"
# 變量名來自外部(如配置文件、用戶輸入)
config_keys = ["config0", "config1", "config2"]
# 動態(tài)讀取所有配置
for key in config_keys:
if key in globals():
print(f"{key}: {globals()[key]}")運(yùn)行結(jié)果:

2、批量初始化
編碼前,無法預(yù)知所有的變量名或需要生成大量相似的變量名,需要通過代碼邏輯批量生成全局變量

3、函數(shù)內(nèi)部修改全局變量
編碼需要時,則需要使用,此處不進(jìn)行舉例說明了。
三、使用注意事項(xiàng)
globals()直接操作全局命名空間,濫用會導(dǎo)致代碼可讀性、可維護(hù)性大幅下降,甚至引發(fā)隱藏bug
1、嚴(yán)重降低代碼可讀性與可維護(hù)性
動態(tài)操作的變量/函數(shù)名是字符串,IDE無法自動補(bǔ)全、跳轉(zhuǎn),代碼審查時難以追蹤變量的定義/修改位置,后期維護(hù)成本高,如下圖所示:

上方globals()里面變量/函數(shù)內(nèi)容很多,某個變量隨時可能被其他方法更改,當(dāng)出現(xiàn)問題時,排查很困難。下方單獨(dú)使用var_dict很清晰能知道變量是在哪里來的,在哪里修改的。
2、易引發(fā)命名沖突與意外覆蓋
全局命名空間包含模塊頂層定義的變量、函數(shù)、類等,globals()動態(tài)創(chuàng)建/修改變量,若名稱與已有重復(fù),則會靜默覆蓋原有對象。
在程序中,如不小心覆蓋內(nèi)置函數(shù),在訪問中可能會遇到報(bào)錯,如下所示

在程序中,如多處動態(tài)給某個變量賦值,則會造成功能錯誤

如上所示,本身天津和重慶都有叫張三的,結(jié)果由于程序后面進(jìn)行了覆蓋,只保留了最后的值。這種在程序中也不存在語法錯誤,在排查bug時會比較麻煩
建議:動態(tài)命名時加前綴,避免與內(nèi)置名、已有變量重名
操作賦值前,先校驗(yàn)是否為已有變量
3、globals()作用區(qū)域理解存在誤區(qū)
python中的幾種作用域可詳見:http://m.fzitv.net/python/344628qyu.htm
很多人會誤以為globals()可以獲取到外層函數(shù)作用域,實(shí)際是未搞清楚外層函數(shù)的變量的作用域?yàn)榍短鬃饔糜?,不會影響全局變量。嵌套函?shù)想訪問外層函數(shù)變量,需要使用nonlocal或local(),而非globals()

4、多線程時,存在線程安全問題
多線程同時通過globals()讀寫全局變量時,同時修改一個變量,會導(dǎo)致數(shù)據(jù)不一致。
count = 0
def increment():
for _ in range(100000):
globals()["count"] += 1
t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
t1.start()
t2.start()
t1.join()
t2.join()
print(count)運(yùn)行結(jié)果是200000,但是實(shí)際結(jié)果是隨機(jī),如下

可以通過加鎖的形式,訪問全局變量從而保證結(jié)果的正確性。
def increment():
for _ in range(100000):
while threading.Lock():
globals()["count"] += 1
5、性能損耗:尤其頻繁操作
globals返回的字典,字典的鍵查找/賦值比直接訪問變量要慢,尤其在頻繁操作時會累積性能損耗。
建議循環(huán)中需要頻繁操作局部變量時,先將賦值給局部變量,操作完成后再賦值給全局變量
到此這篇關(guān)于python中的內(nèi)置globals()詳解的文章就介紹到這了,更多相關(guān)python內(nèi)置globals()內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python pandas RFM模型應(yīng)用實(shí)例詳解
這篇文章主要介紹了Python pandas RFM模型應(yīng)用,結(jié)合實(shí)例形式詳細(xì)分析了pandas RFM模型的概念、原理、應(yīng)用及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-11-11
Python異常處理try語句應(yīng)用技巧實(shí)例探究
異常處理在Python中是至關(guān)重要的,try-except是用于捕獲和處理異常的核心機(jī)制之一,本文就帶大家深入了解如何使用try-except,處理各種異常情況2024-01-01
Python使用unicodedata實(shí)現(xiàn)字符串標(biāo)準(zhǔn)化
這篇文章主要來和大家聊一聊 Python 的一個內(nèi)置模塊:unicodedata,它是專門用來處理 unicode 字符串的,下面就一起來看看它的用法吧2023-06-06
python循環(huán)接收http請求數(shù)據(jù)方式
這篇文章主要介紹了python循環(huán)接收http請求數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06

