The difference between "global" and "import __main__"

I have identified three functions that should change the global variable x .

 def changeXto1(): global x x = 1 def changeXto2(): from __main__ import x x = 2 def changeXto3(): import __main__ __main__.x = 3 x = 0 print x changeXto1() print x changeXto2() print x changeXto3() print x 

It gives the result:

 0 1 1 3 

changeXto1 uses the usual global operator. The result is as expected x == 1. changeXto2 uses from __main__ import for address x . This does not work. Subsequently, x remains 1. changeXto3 uses import main for address x through __main__.x As a result, the result is 3, as expected.

Why does from __main__ import work in changeXto2 and import __main__ works in changeXto3 ? Why do we need a global operator in Python if we can access global variables also with the __main__ module?

+7
source share
2 answers

This is due to the way Python converts your code into bytecode (compilation step).

When compiling a function, Python processes the entire variable, which is assigned as a local variable, and performs optimization to reduce the number of name searches that it would have to perform. Each local variable is assigned an index, and when the function is called, their value will be stored in the local stack array addressed by the index. The compiler will issue the code LOAD_FAST and STORE_FAST to access the variable.

The global syntax instead indicates to the compiler that even if a variable is assigned a value, it cannot be considered as a local variable, it should not be assigned an index. Instead, it will use the code LOAD_GLOBAL and STORE_GLOBAL to access the variable. These opcodes are slower, since they use the name to search in as many dictionaries as possible (locals, globals).

If the variable is read-only, the compiler always allocates LOAD_GLOBAL , because it does not know if it should be a local or global variable, and therefore, assume that it is global.

So, in your first function, global x tells the compiler that you want it to treat write access to x as a global variable record instead of a local variable. The operation codes for the function make it clear:

 >>> dis.dis(changeXto1) 3 0 LOAD_CONST 1 (1) 3 STORE_GLOBAL 0 (x) 6 LOAD_CONST 0 (None) 9 RETURN_VALUE 

In the third example, you import the __main__ module into a local variable named __main__ , and then assign its field x . Since the module is an object that saves all the top-level mapping as fields, you assign the variable x in the __main__ module. And, as you discovered, the __main__ module fields directly display the values ​​in the globals() dictionary, because your code is defined in the __main__ module. The operation codes show that you do not have direct access to x :

 >>> dis.dis(changeXto3) 2 0 LOAD_CONST 1 (-1) 3 LOAD_CONST 0 (None) 6 IMPORT_NAME 0 (__main__) 9 STORE_FAST 0 (__main__) 3 12 LOAD_CONST 2 (3) 15 LOAD_FAST 0 (__main__) 18 STORE_ATTR 1 (x) 21 LOAD_CONST 0 (None) 24 RETURN_VALUE 

The second example is interesting. Since you assign the value to the variable x , the compiler assumes that it is a local variable and performs the optimization. Then from __main__ import x imports the __main__ module and creates a new binding of the value x in the __main__ module to a local variable named x . This is always the case, from ${module} import ${name} just create a new binding of the current namespace. When you assign a new value to the variable x , you simply change the current binding, not the binding in the __main__ module, which is not connected (although if the value is changed and you mutate it, the change will be visible through all the bindings). Here are the operation codes:

 >>> dis.dis(f2) 2 0 LOAD_CONST 1 (-1) 3 LOAD_CONST 2 (('x',)) 6 IMPORT_NAME 0 (__main__) 9 IMPORT_FROM 1 (x) 12 STORE_FAST 0 (x) 15 POP_TOP 3 16 LOAD_CONST 3 (2) 19 STORE_FAST 0 (x) 22 LOAD_CONST 0 (None) 25 RETURN_VALUE 

A good way to think about this is that in Python all assignments bind a name to a value in a dictionary, and dereferencing just searches the dictionary (this is an approximate approximation, but pretty close to the conceptual model). By executing obj.field , you are viewing a hidden dictionary obj (accessible via obj.__dict__ ) for the "field" key.

When you have a bare variable name, it is looked up in the locals() dictionary, and then the globals() dictionary if it is different (they are the same when the code is executed at the module level). For assignment, it always places the binding in the locals() dictionary, unless you have declared that you want to access globally by executing global ${name} (this syntax also works at the top level).

So, translating your function, it's almost like you wrote:

 # NOTE: this is valid Python code, but is less optimal than # the original code. It is here only for demonstration. def changeXto1(): globals()['x'] = 1 def changeXto2(): locals()['x'] = __import__('__main__').__dict__['x'] locals()['x'] = 2 def changeXto3(): locals()['__main__'] = __import__('__main__') locals()['__main__'].__dict__['x'] = 3 
+10
source

Why does from __main__ import not work in changeXto2 , and import __main__ works in changeXto3 ?

It works great, it just doesn't do what you want. It copies the name and value into the local namespace instead of using the __main__ namespace.

Why do we need a global operator in Python if we can access global variables also with the __main__ module?

Because they do the same only when your code runs in __main__ . If you run, say, othermodule after import, then __main__ will refer to the main script, not othermodule .

+8
source

All Articles