python - 关于变量作用域的棘手 Python 问题

标签 python python-3.x scope

我的 Python 项目中经常有需要在模块之间共享的变量。我知道这可以通过函数调用的 args 来完成,但有时,创建一个 global_vars.py 模块并添加需要在那里共享的任何变量会更方便,这就是我经常做的事情。然后可以从任何其他模块导入这些变量,并轻松共享。这通常对我来说效果很好。但有时,一些意想不到的事情会发生。

例如:

我有 3 个文件:

main.py:

from global_vars import var
from mod import modify_variable

print(f"{hex(id(var))} - address of var in main module after import")
modify_variable()
print(f"{hex(id(var))} - address of var in main module after modify_variable() call")

global_vars.py:

var = 'hi'
print(f"{hex(id(var))} - address of var during import")

mod.py: 从 global_vars 导入变量

def modify_variable():
    global var

    print(f"{hex(id(var))} - address of var before modifying it in modify_variable()")
    var = 'hello'
    print(f"{hex(id(var))} - address of var after modifying it in modify_variable()")

如果使用 Python3 运行 main.py,您会得到如下所示的输出:

0x7f0f993bb7f0 - address of var during import
0x7f0f993bb7f0 - address of var in main module after import
0x7f0f993bb7f0 - address of var before modifying it in modify_variable()
0x7f0f993bb870 - address of var after modifying it in modify_variable()
0x7f0f993bb7f0 - address of var in main module after modify_variable() call

基本上,在我们调用 modify_variable 之前,一切都会按预期运行。 在 modify_variable 中,var 的地址按照我们的预期开始。 然后我们为其分配一个新字符串。这做了一些事情:

  • 它在内存中创建一个全新的字符串
  • 它将 var 更改为指向这个新字符串的地址
  • 垃圾收集器大概会在某个时候清理旧字符串的地址
鉴于此,我希望对 var 地址的最后一次检查也将指向新字符串的地址。因此,我希望结果如下所示:

0x7f0f993bb7f0 - address of var during import
0x7f0f993bb7f0 - address of var in main module after import
0x7f0f993bb7f0 - address of var before modifying it in modify_variable()
0x7f0f993bb870 - address of var after modifying it in modify_variable()
0x7f0f993bb870 - address of var in main module after modify_variable() call

但事实并非如此。 modify_variable 中的 var 和 main.py 中的 var 现在指向完全不同的地址,并且不能再共享相同的数据。

发生了什么?根据我的阅读,如果我没有在 mod.py 中使用 global var,我可能会创建一个同名的局部变量全局 var 变量,这可能会导致上述症状,但像我一样使用 global var 可确保我处理的是 global_vars.var,不是吗?

最佳答案

规范

Python 并没有真正的全局变量。这是 python defines作为全局:

If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.)

还有global声明的意思是:

It means that the listed identifiers are to be interpreted as globals.

即作为模块级变量。

如果你看看import是:

  1. find the module specified in the from clause, loading and initializing it if necessary;
  2. for each of the identifiers specified in the import clauses:

    [...]

    1. [...] a reference to that value is stored in the local namespace, using the name in the as clause if it is present, otherwise using the attribute name

您的案例

因此,global_vars.var指向地址0x7f0f993bb7f0,当你将其导入mod时,它就变成了mod.var 也指向 0x7f0f993bb7f0

当你执行global var时,你告诉Python解析器将var绑定(bind)到mod.var,然后使用var = 'hello',您使 mod.var 指向 0x7f0f993bb870

但是在 main.py 中,var 绑定(bind)到 main.var,它是由 import 分配的语句到 global_vars.var,即 0x7f0f993bb7f0

做什么

将全局变量实现为单个全局对象的属性:

g.py:

class Global:
    pass

g = Global()

setattr(g, 'var', 'hi')

print(f"{hex(id(g.var))} - address of var during import")

mod.py:

from g import g

def modify_variable():
    print(f"{hex(id(g.var))} - address of var before modifying it in modify_variable()")
    g.var = 'hello'
    print(f"{hex(id(g.var))} - address of var after modifying it in modify_variable()")

main.py:

#!/usr/bin/python3.6
from g import g
from mod import modify_variable

print(f"{hex(id(g.var))} - address of var in main module after import")
modify_variable()
print(f"{hex(id(g.var))} - address of var in main module after modify_variable() call")

输出:

0x7ff2a47a1998 - address of var during import
0x7ff2a47a1998 - address of var in main module after import
0x7ff2a47a1998 - address of var before modifying it in modify_variable()
0x7ff2a47ae500 - address of var after modifying it in modify_variable()
0x7ff2a47ae500 - address of var in main module after modify_variable() call

关于python - 关于变量作用域的棘手 Python 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59906128/

相关文章:

Python打印函数不按顺序打印

python - 日志中遇到无效值

python - 使用 Python 替换或交换文件中的子字符串

python - 亚马逊cloudfront失效请求python编码问题

python - 如何从 seaborn/matplotlib 图中删除或隐藏 x 轴标签

javascript - 范围 : [variable-name] ='@' mean in Angular? 是什么意思

python - 了解 Python 中的异步/多处理

python - 在 Python 3 中将 8 位整数编码为文本

javascript - 谷歌地图错误标记获取事件属性

python - 从装饰器访问 self