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

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

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

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

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

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

4、多線程時,存在線程安全問題
多線程同時通過globals()讀寫全局變量時,同時修改一個變量,會導致數據不一致。
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)運行結果是200000,但是實際結果是隨機,如下

可以通過加鎖的形式,訪問全局變量從而保證結果的正確性。
def increment():
for _ in range(100000):
while threading.Lock():
globals()["count"] += 1
5、性能損耗:尤其頻繁操作
globals返回的字典,字典的鍵查找/賦值比直接訪問變量要慢,尤其在頻繁操作時會累積性能損耗。
建議循環(huán)中需要頻繁操作局部變量時,先將賦值給局部變量,操作完成后再賦值給全局變量
到此這篇關于python中的內置globals()詳解的文章就介紹到這了,更多相關python內置globals()內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

