Python中import外部模塊全局變量修改規則及踩坑


最近碰到一個import外部文件全局變量修改后未符合預期效果的問題,簡要描述如下:

有env.py, test.py, dal.py三個文件,env.py 中定義了DEBUG=False的全局變量,dal.py中部分代碼會根據DEBUG取值決定是否走調試邏輯,在test.py中通過from env import DEBUG后,設置DEBUG=True,然而在dal.py中實際使用DEBUG時卻發現DEBUG取值依然是False,並沒有修改成功。簡化代碼如下:

# env.py
DEBUG = False
# dal.py
from env import DEBUG


def test():
    if DEBUG:
        print('DEBUG logic')
    else:
        print('online logic')

# test.py
import dal
from env import DEBUG


if __name__ == '__main__':
    DEBUG = True
    dal.test()

執行結果:

$ python test.py
online logic

一時之間覺得非常奇怪,探究了一下其具體原因,發現實際要想修改import的其他模塊全局變量取值並生效,還真有些講究在里面,這里總結分享一下。

全局變量的修改對於值類型和引用類型規則並不相同,因而以下舉例中同時定義了gx/gy作為值類型代表,gdctx/gdcty作為引用類型代表,同時為了跟蹤是否指向同一對象,使用id函數打印出了每一變量的對象id。

定義以下下代碼文件:

# env.py
gx = 'env'
gy = 'env'
gdctx = {'env': 'env'}
gdcty = {'env': 'env'}

print('gx:{}|{}\tgy:{}|{}\tgdctx:{}|{}\tgdcty:{}|{}\tin env'.format(id(gx), gx, id(gy), gy, id(gdctx), gdctx, id(gdcty), gdcty))
# mods.py
from env import gx, gy, gdctx, gdcty
import env


def print_env():
    print('gx:{}|{}\tgy:{}|{}\tgdctx:{}|{}\tgdcty:{}|{}\tin mods'.format(id(gx), gx, id(gy), gy, id(gdctx), gdctx, id(gdcty), gdcty))
    print('gx:{}|{}\tgy:{}|{}\tgdctx:{}|{}\tgdcty:{}|{}\tin mods.env'.format(id(env.gx), env.gx, id(env.gy), env.gy, id(env.gdctx), env.gdctx, id(env.gdcty), env.gdcty))

test1.py中通過import env中的全局變量直接進行修改:

# test1.py
from env import gx, gy, gdctx, gdcty
import env
from mods import print_env


gx = 'test1' # 實際生成了新的值對象,對象id發生變動
gdctx['env'] = 'test1' # 對引用類型dict修改了k/v,對象id不變

def test():
    gy = 'test1' # 未添加global聲明,實際生成了新的局部變量gy,不影響全局變量
    gdcty = {} # 未添加global聲明,實際生成了新的局部變量gdcty,不影響全局變量

if __name__ == '__main__':
    print('gx:{}|{}\tgy:{}|{}\tgdctx:{}|{}\tgdcty:{}|{}\tin test1'.format(id(gx), gx, id(gy), gy, id(gdctx), gdctx, id(gdcty), gdcty))
    print('gx:{}|{}\tgy:{}|{}\tgdctx:{}|{}\tgdcty:{}|{}\tin test1.env'.format(id(env.gx), env.gx, id(env.gy), env.gy, id(env.gdctx), env.gdctx, id(env.gdcty), env.gdcty))
    print_env()

執行 test1.py:

$ python test1.py
gx:4508684848|env    gy:4508684848|env    gdctx:4509665632|{'env': 'env'}      gdcty:4509713664|{'env': 'env'}    in env  # env.py中的對象id及取值
gx:4510060784|test1  gy:4508684848|env    gdctx:4509665632|{'env': 'test1'}    gdcty:4509713664|{'env': 'env'}    in test1 # test1.py中gx已經是不同的對象id與取值, gdctx內容發生變化,但依然指向同一對象,gy、gdcty不受影響
gx:4508684848|env    gy:4508684848|env    gdctx:4509665632|{'env': 'test1'}    gdcty:4509713664|{'env': 'env'}    in test1.env # 通過env.* 形式直接引用env中的變量,對象id不變,gdctx內容發生變化
gx:4508684848|env    gy:4508684848|env    gdctx:4509665632|{'env': 'test1'}    gdcty:4509713664|{'env': 'env'}    in mods # 保持指向最開始import時的env.*對象,注意gx不受test1.py中的修改影響
gx:4508684848|env    gy:4508684848|env    gdctx:4509665632|{'env': 'test1'}    gdcty:4509713664|{'env': 'env'}    in mods.env # env.*形式引用env.py中的相同變量,對象id不變,gdctx內容發生變化

 

test2.py中通過env.*、global聲明的形式修改全局變量取值:

# test2.py
from env import gx, gy, gdctx, gdcty
import env
from mods import print_env


env.gx = 'test2' # 通過env.gx引用值類型,相當於將env.gx指向新生成的值對象,env.gx對象id發生變化
env.gdctx = {'test2': 'test2'} # 通過env.gdctx引用引用類型,將其指向一個新的dict對象,env.gdctx對象id發生變化

def test():
    global gy, gdcty
    gy = 'test2' # 添加global聲明后指向全局變量gy,將其指向新對象,gy對象id發生變化
    gdcty = {} # 指向新的dict對象,gdcty id發生變化

if __name__ == '__main__':
    test()
    print('gx:{}|{}\tgy:{}|{}\tgdctx:{}|{}\tgdcty:{}|{}\tin test2'.format(id(gx), gx, id(gy), gy, id(gdctx), gdctx, id(gdcty), gdcty))
    print('gx:{}|{}\tgy:{}|{}\tgdctx:{}|{}\tgdcty:{}|{}\tin test2.env'.format(id(env.gx), env.gx, id(env.gy), env.gy, id(env.gdctx), env.gdctx, id(env.gdcty), env.gdcty))
    print_env()

執行結果:

gx:4502704816|env    gy:4502704816|env    gdctx:4503685552|{'env': 'env'}        gdcty:4503733584|{'env': 'env'}    in env
gx:4502704816|env    gy:4504080752|test2  gdctx:4503685552|{'env': 'env'}        gdcty:4502670384|{}    in test2 # gx、gdctx保持import時的原值不受贏下,gy指向新值對象,gdcty指向新的dict對象
gx:4504080752|test2  gy:4502704816|env    gdctx:4502670144|{'test2': 'test2'}    gdcty:4503733584|{'env': 'env'}    in test2.env # env.gx、env.gdctx指向新的對象,env.gy、env.gdcy不受影響
gx:4502704816|env    gy:4502704816|env    gdctx:4503685552|{'env': 'env'}        gdcty:4503733584|{'env': 'env'}    in mods # 保持指向最開始import時的env.*對象,無任何變動
gx:4504080752|test2  gy:4502704816|env    gdctx:4502670144|{'test2': 'test2'}    gdcty:4503733584|{'env': 'env'}    in mods.env # env.gx、env.gdctx已經指向新的對象,env.gy、env.gdcty不變

通過test1.py、test2.py的執行結果可以得出以下結論:

1,from XX import YY的方式導入全局變量后,如果XX.YY取值在某一模塊發生了修改導致其指向的對象發生了變化(對象id不同),其他模塊引入的YY並不會同步修改,而是指向最初的取值。因而考慮全局變量修改的情況下,應使用import XX,而后使用XX.YY的方式進行引用。

2,值類型賦不同值肯定會導致對象id變化,因而無法跨文件傳遞修改內容,引用類型如果整體被指向新對象會導致對象id變化,同樣無法跨文件傳遞修改,但是只修改引用對象本身的某部分內容則不會生成新對象,修改可以成功跨文件傳遞。

3,函數中引用全局變量,如果只是讀取,會先查找同名本地變量,而后全局變量,但是如果涉及賦值、修改其語義則是定義一個新的局部變量,此時要記住使用global聲明對應的全局變量。

 

轉載請注明出處,原文地址:https://www.cnblogs.com/AcAc-t/p/python_global_import_rule.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM