简体   繁体   English

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

[英]Tricky Python question about variable scope

I often have variables in my Python projects that I need to share between modules.我的 Python 项目中经常有需要在模块之间共享的变量。 I know this can be done with args to function calls, but sometimes, it's more convenient to create a single global_vars.py module, and add any variables that need to be shared there, which is what I often end up doing.我知道这可以通过 args 来完成函数调用,但有时,创建一个 global_vars.py 模块更方便,并添加需要在那里共享的任何变量,这是我经常最终做的。 These variables can then be imported from any other module, and easily shared.然后可以从任何其他模块导入这些变量,并轻松共享。 This generally works fine for me.这通常对我有用。 Sometimes though, some unexpected stuff happens.但是,有时会发生一些意想不到的事情。

Ex:前任:

I have 3 files:我有3个文件:

main.py:主要.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: global_vars.py:

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

mod.py: from global_vars import var mod.py: from global_vars import var

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()")

If you run main.py with Python3, you get output that looks like this:如果您使用 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

Basically, everything behaves as expected until we call modify_variable .基本上,一切都按预期运行,直到我们调用modify_variable Within modify_variable , the address of var starts out as we expect.modify_variablevar的地址按照我们的预期开始。 Then we assign a new string to it.然后我们为其分配一个新字符串。 This does a few things:这做了几件事:

  • It creates a brand new string in memory它在内存中创建一个全新的字符串
  • It changes var to point to the address of this new string它将var更改为指向这个新字符串的地址
  • The garbage collector would presumably, at some point, clean up the old string's address垃圾收集器可能会在某个时候清理旧字符串的地址

Given this, I would expect that the last check of the address of var would also point to the address of the new string.鉴于此,我希望最后一次检查var的地址也指向新字符串的地址。 Therefore, I would expect the result to look like this:因此,我希望结果如下所示:

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

but it doesn't.但事实并非如此。 var within modify_variable , and var within main.py now point to completely different addresses, and can no longer share the same data. modify_variable varmodify_variable var现在指向完全不同的地址,并且不能再共享相同的数据。

What happened?发生了什么? From what I've read, if I hadn't used global var in mod.py , it was possible that I would have created a local variable with the same name as the global var variable, which could cause the symptoms above, but using global var as I do ensures I'm dealing with global_vars.var , doesn't it?从我读到的内容来看,如果我没有在mod.py使用global var ,我可能会创建一个与 global var变量同名的局部变量,这可能会导致上述症状,但使用我所做的global var确保我正在处理global_vars.var ,不是吗?

The spec规格

Python doesn't really have global variables. Python 并没有真正的全局变量。 Here's what python defines as global:这是python 定义为全局的内容:

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.) (模块代码块的变量是局部的和全局的。)

And the global statement means: global声明意味着:

It means that the listed identifiers are to be interpreted as globals.这意味着列出的标识符将被解释为全局变量。

ie as module-level variables.即作为模块级变量。

If you look at what import does:如果您查看import作用:

  1. find the module specified in the from clause, loading and initializing it if necessary;找到 from 子句中指定的模块,必要时加载和初始化它;
  2. for each of the identifiers specified in the import clauses:对于 import 子句中指定的每个标识符:

    [...] [...]

    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 [...] 对该值的引用存储在本地命名空间中,如果存在则使用 as 子句中的名称,否则使用属性名称

Your case你的情况

So, global_vars.var points to address 0x7f0f993bb7f0 , and when you import it to mod , it becomes mod.var which also points to 0x7f0f993bb7f0 .所以, global_vars.var指向地址0x7f0f993bb7f0 ,当你将它导入mod ,它变成mod.var ,它也指向0x7f0f993bb7f0

When you do global var you tell the Python parser to bind var to mod.var , and then with var = 'hello' , you make mod.var point to 0x7f0f993bb870 .当您执行global var您告诉 Python 解析器将var绑定到mod.var ,然后使用var = 'hello' ,您使mod.var指向0x7f0f993bb870

But in main.py , var binds to main.var , which was assigned by the import statement to global_vars.var , namely 0x7f0f993bb7f0 .但是在main.pyvar绑定到main.var ,它由import语句分配给global_vars.var ,即0x7f0f993bb7f0

What to do该怎么办

Implement globals as attributes of a single global object:将全局变量实现为单个全局对象的属性:

g.py : g.py :

class Global:
    pass

g = Global()

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

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

mod.py : 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 : 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")

Output:输出:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM