2

The common explanation given (as in 12270954 and 710551) is that from X import * clutters up the namespace, and hence is not recommended. However, the following example shows that there is something more to it than just cluttering the namespace.

Consider the following code:

x1.py:

g_c = 5
class TestClass():
    def run(self):
        global g_c
        g_c = 1
        print(g_c) # prints 1

x2.py:

from x1 import *
t = TestClass()
t.run()
print(g_c) # prints 5, why?

x3.py:

import x1
t = x1.TestClass()
t.run()
print(x1.g_c) # prints 1

The results for running x2.py and x3.py are different (on python 3.6.8). Can someone please explain why the two imports are behaving differently?

Extra Notes: (to demonstrate the note mentioned by @tdelaney about lists)

Change the assignments in x1.py like this:

g_c = [5]
...
g_c.append(1)

And now, both x2.py and x3.py give the same answer. It is only when an atomic type is being used that the problem arises.

R71
  • 4,283
  • 7
  • 32
  • 60
  • 3
    because starred imports *assign to new variables in the module they are imported in*. They do two different things, they aren't really behaving differently. You can think of starred imports and the `from module import some_var` as sugar for `import module; some_var = module.some_var; del module` – juanpa.arrivillaga Apr 11 '20 at 06:41
  • 1
    To me, the reason to avoid `from X import *` is for future readability. When I see a fairly ambiguously named function like `find` I'd like to know where it came from without poking through the imports. – tdelaney Apr 11 '20 at 06:56
  • Thanks @juanpa.arrivillaga, the main issue is that for "import *" the variables are copied and not referenced. Is this mentioned anywhere in the python documentation explicitly? – R71 Apr 11 '20 at 12:51
  • @R71 that's *always* how variables works. Note, the *object* isn't copied, a *new variables is added to the namespace*. It couldn't work any other way without special-casing the semantics of assignment. – juanpa.arrivillaga Apr 11 '20 at 18:14

5 Answers5

3

You can imagine from x1 import * doing something approximately equal to

import m1
globals.update(m1.__dict__)
del m1

In other words it imports the module and then copies all the globals into your namespace. The function and classes inside the module however will keep referring to their original globals, not to the copies in your namespace.

Note the "implementation" above is not completely correct as not all names are copied and there are other subtleties, but it's I think an easy way to understand the general behavior.

The difference becomes evident when the module changes its globals but you don't see the change reflected in your global namespace.

After executing from x1 import * the variables sys.modules["x1"].g_c and g_c are two distinct variables with the same value; code in the module is referring to the one inside the module, not to your copy.

R71
  • 4,283
  • 7
  • 32
  • 60
6502
  • 112,025
  • 15
  • 165
  • 265
  • I highlighted the word "copies" in your answer, I think that is the keyword to understand for this question. But can you give a reference from the python manual which mentions this explicitly, I could not find it. – R71 Apr 11 '20 at 12:45
  • @R71: the technical term used is Python documentation is "binding"; in https://docs.python.org/3/reference/executionmodel.html#structure-of-a-program the `from ... import *` is explained as creating bindings for all the names (except those starting with `_`). This is where the "copies" are made. – 6502 Apr 11 '20 at 12:51
  • Yes, it is important to point out, "binding" is just normal assignment, exactly like `a = 42; b = a; b = 0; print(a)` prints 42 not 0 – juanpa.arrivillaga Apr 11 '20 at 18:48
2

When you import X, you will have to use X to call a function inside X, ie:

X.func1()

But when you use from X import *, you import all names in the module's namespace that don't begin with _ or all names in module.__all__ , and you don't require to use X to call the function, ie:

func1()

In x2.py when you did:

from x1 import *

you just imported every thing from x1 , ie , including the variables. This means that your altered the namespace of the current script (x2). So when you call:

print(g_c)

it prints :

5

which is called from the current scripts namespace.
On the run of the class TestClass() in x2 , it changed the value of g_c in x1, but not in x2.

This indicates the Disadvantages of using Global Variables.
When variables are declared as global, then they remain in the memory till program execution is completed. That is, your global declaration is only valid inside x1. The declaration is valid inside x1's namespace.


And when you did:

import x1
t = x1.TestClass()
t.run()

This import x1 as a module and everything inside isin't imported unless you call it. And you did call the class TestClass(), ran it, resulting g_c inside the class being made a global variable. So then for x1, g_c is 1 since it was globally defined and the current script doesn't know x1 has a g_c = 5 since it wasn't imported/called.
On print(x1.g_c), it calls for the value of g_c in x1's namespace, which is 1.


Joshua Varghese
  • 5,082
  • 1
  • 13
  • 34
  • "you have imported all callables in X " no, you import all names in the module's namespace that don't begin with `_`, or all names in `module.__all__` – juanpa.arrivillaga Apr 11 '20 at 07:04
  • A big answer, but it doesnt answer the main question, why x2 is printing a different value for g_c. Also, the answer given is incorrect and not verified before writing it - "I believe, in x2 if you give: print(x1.g_c) it would print 1"., Actually it gives an error because I have done "from x1 import *". – R71 Apr 11 '20 at 12:36
  • Yes thats an error. i'll remove it. is that the wrong part you said? reason is explained – Joshua Varghese Apr 11 '20 at 12:42
  • yes that was the wrong part. But anyway, a very long answer distracts from the main question asked, which was briefly answered by @6502. – R71 Apr 11 '20 at 13:04
2

from X import * rebinds the objects in X's global namespace to the current module's namespace. If one of these variables is reassigned, the reassignment is private to the current namespace.

The import doesn't change the imported objects, though, it just adds a new reference. So for instance, the global namespace for class TestClass is still the x1.py module. TestClass.run always changes x1.g_c (its global namespace) regardless of which module calls it.

In x2.py, you import the original 5 referenced by x1.g_c to x2.g_c. But reassignment of x1.g_c doesn't affect x2.g_c which is a variable in a different namespace.

Suppose conversely that x1.g_c was a list and your code was appending to the list. In that case, all modules would see the appends because you are modifying the object referenced by x1.g_c, not reassigning it.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
1

Let us take an example where you are importing the module suman in Python. It brings the entire module into your workspace i.e. In the import suman statement it does not give you access to any methods or function associated with like for example if their is a function display_name() in the suman module then you have to import it like suman.display_name()

In from suman import * it brings all of the names inside suman module (like display() for example) into your module. Now you can access those names without a prefix of name of your module like this:

from suman import *
print(display_name())
Joshua Varghese
  • 5,082
  • 1
  • 13
  • 34
1

The keys of this behavior is following:

  1. Python makes namespaces for each files
  2. global X makes variable X refer to the object that lives in the namespace where global X is declared

When t.run() is executed in x2.py, global g_c is evaluated and this makes g_c refers to the object in the namespace for x1.py not x2.py.

Trying the following snippet would be help.

x2_.py

from x1 import *
t = TestClass()
t.run()
print(g_c)  # prints 5

import x1
print(x1.g_c) # this print 1
hotoku
  • 151
  • 9