globals()['variable'] =在.py文件中设置的值在其他.py文件中使用时不反映值更改

时间:2019-01-25 06:40:07

标签: python

globalEx1.py

globals()['a']='100'

def setvalue(val):
    globals()['a'] = val

globalEx2.py

from globalEx1 import *

print a
setvalue('200')
print a 

执行globalEx2.py时:

输出:

100
100

如何使用函数更改globals['a']的值,以使其在.py文件中反映出来?

3 个答案:

答案 0 :(得分:4)

每个模块都有自己的全局变量。 Python的行为完全符合预期。更新globalEx1的{​​{1}}以指向其他内容不会影响a的{​​{1}}指向的位置。

有多种解决方法,具体取决于您想要的东西。

  1. globalEx2调用后重新导入a
  2. a并进行分配,例如setvalue()
  3. return a并使用a = setvalue()代替import globalEx1。 (或使用globalEx1.a和更短的名称。)
  4. a的{​​{1}}作为import globalEx1 as的参数,并在其上设置值。
  5. 使globalEx2是一个包含对象的可变对象,例如列表,字典或globals(),并在setvalue中对其进行突变。
  6. a中使用types.SimpleNamespace从其堆栈框架中获取调用者的全局变量。 (方便,但很脆弱。)
  

最后一个选项对我来说很合适。它可以用最少的代码更改来完成这项工作,但是我可以使用相同的方式更新多个模块的全局变量吗?还是只给我呼叫者的全局变量?

选项6实际上是风险最高的。调用者本身基本上成为该函数的隐藏参数,因此,来自其他模块的装饰器之类的东西可能会在没有警告的情况下中断它。选项4只是使该隐藏参数明确,因此它并不那么脆弱。

如果您需要此功能在两个以上的模块中使用,则选项6不够好,因为它只能为您提供当前的调用堆栈。对于您似乎想做的事情,选项3可能是最可靠的。


  

选项1如何工作?我的意思是关于再次运行->“ from globalEx1 import *”,因为我有很多像'a'这样的变量。

模块在首次导入时成为对象,并且已保存在setvalue缓存中,因此再次导入不会再次执行该模块。 inspect(甚至使用setvalue)仅从该模块对象获取属性,并将它们添加到本地范围(如果在顶级(即,在任何定义之外),则为模块全局变量。 )

模块对象的sys.modules基本上是其全局变量,因此任何更改模块全局变量的函数都会影响所得模块对象的attrs,即使它是在导入模块之后完成的。

  

我们无法从python函数的“ globalEx1 import *”中执行此操作吗?

仅在顶层允许使用star语法。但是请记住,它只是从模块对象读取属性。这样您就可以了解所有模块属性,例如

from ... import

*相比,您可以获得更多收益。默认情况下,它不返回以__dict__开头的名称,否则不返回在return vars(globalEx1) 中指定的子集。您可以使用dict理解过滤结果dict,甚至可以*过滤带有结果的其他模块的globals dict。

但是您可以只使用exec使其成为顶层,而不是重新实现此过滤逻辑。然后,您会得到的唯一奇怪的密钥是_

__all__

然后您可以.update()或其他任何方式。

像这样使用__builtins__可能被认为是不好的形式,但坦白地说,namespace = {} exec('from globalEx1 import *', namespace) del namespace['__builtins__'] return namespace 也是这样。

答案 1 :(得分:1)

这是一个有趣的问题,与字符串不可变有关。行MyMainObjectfrom globalEx1 import *模块中创建两个引用:globalEx2asetvalue最初指的是与globalEx2.a相同的字符串对象,因为这就是导入的工作方式。

但是,一旦调用globalEx1.a(在setvalue的全局变量上运行),由globalEx1引用的值将被另一个字符串对象替换。由于字符串是不可变的,因此无法就地执行此操作。 globalEx1.a的值仍应绑定到原始字符串对象。

您可以在此处找到几种解决方法。最pythonic的是修复globalEx2.a中的导入:

globalEx2

另一种选择是对import globalEx1 print globalEx1.a globalEx1.setvalue('200') print globalEx1.a 使用可变容器,并访问该容器:

a
globals()['a']=['100']

def setvalue(val):
    globals()['a'][0] = val

第三个更普遍的选择是将from globalEx1 import * print a[0] setvalue('200') print a[0] 的{​​{1}}复制为原始函数,但将其globalEx2属性设置为{{1} }代替setvalue

__globals__

您必须进行复制的原因是globalEx2是只读属性,而且您也不想弄乱globalEx1中的函数。参见https://stackoverflow.com/a/13503277/2988730

答案 2 :(得分:0)

全局变量在import语句开头仅被导入一次。因此,如果全局变量是不可变的对象,如str,int等,则不会反映任何更新。但是,如果全局变量是可变对象(如列表等),则将反映更新。例如,

globalEx1.py:

globals()['a']=[100]

def setvalue(val):
    globals()['a'][0] = val

输出将按预期更改:

[100]
[200]

在旁边

像普通变量一样定义全局变量更容易:

a = [100]

def setvalue(value):
    a[0] = value

或者在编辑不可变对象的值时:

a = 100

def setvalue(value):
    global a
    a = value