Python中import外部模块全局变量修改规则及踩坑
Posted To be a better programmer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
以上是关于Python中import外部模块全局变量修改规则及踩坑的主要内容,如果未能解决你的问题,请参考以下文章