Reputation: 1371
I have had a problem with the difference between import module
and from module import name1, name2 ...
in Python. I am a newcomer (last week) to Python, using Python 3.6 on Windows-64.
In the Python Tutorial there is a short discussion of these two import
approaches. It is stated that from module import *
is not recommended because of the danger of polluting the current namespace. However, there is no indication that there is any material operational difference between import module
and from module import name1, name2..
, with the implication that this is a matter of preference or convenience.
However, there seems to be a big difference in practice. Consider this module named ModuleA, defining a global variable and a function:
# ModuleA
iGlobalA = 0
def fA():
iGlobalA += 1
print( "MA: iGlobalA=", iGlobalA )
print( "Module A Initialised, iGlobalA=", iGlobalA )
Using import ModuleA
creates a separate ModuleA
namespace. Now the members of that module are available as public members of the namespace, like this:
import ModuleA as MA
def fX():
print( "MX: Before, ModuleA.iGlobalA=", MA.iGlobalA )
MA.fA()
print( "MX: After 1, ModuleA.iGlobalA=", MA.iGlobalA )
MA.fA()
print( "MX: After 2, ModuleA.iGlobalA=", MA.iGlobalA )
fX()
This is the output:
MA: Initialised, iGlobalA= 100
MX: Before, ModuleA.iGlobalA= 100
MA: iGlobalA incremented to 101
MX: After 1, ModuleA.iGlobalA= 101
MA: iGlobalA incremented to 102
MX: After 2, ModuleA.iGlobalA= 102
which is exactly as expected. Contrast this with ModuleY which uses the form from ModuleA import fA, iGlobalA
and then refers to these members of ModuleA without qualification:
# ModuleY
from ModuleA import fA, iGlobalA
def fY():
print( "MY: Before, ModuleA.iGlobalA=", iGlobalA )
fA()
print( "MY: After 1, ModuleA.iGlobalA=", iGlobalA )
fA()
print( "MY: After 2, ModuleA.iGlobalA=", iGlobalA )
fY()
In this case the output is:
MA: Initialised, iGlobalA= 100
MY: Before, ModuleA.iGlobalA= 100
MA: iGlobalA incremented to 101
MY: After 1, ModuleA.iGlobalA= 100
MA: iGlobalA incremented to 102
MY: After 2, ModuleA.iGlobalA= 100
In this case the global variable iGlobalA
is imported as a copy from ModuleA after ModuleA is initialised, and becomes a completely separate variable from ModuleA.iGlobalA
. It is also true that function fA
is imported as a reference to the function in ModuleA as defined at the time of import - if the function is reassigned at some later point within ModuleA, then the reference to fA()
in the importing module remains unchanged, pointing only to the function as originally imported.
I would have thought these differences between these import syntaxes should be more clearly stated in the documentation. It also means that someone designing a library module needs to specify how that module should be imported.
Edit - after comment by @abdullah-ahmed-ghaznavi These are my questions
Upvotes: 4
Views: 253
Reputation: 77912
In this case the global variable iGlobalA is imported as a copy from ModuleA
Nope. Just after from ModuleA import iGlobalA
, both ModuleA.iGlobalA
and ModuleY.iGlobalA
point to the very same object - you can check this by printing id(iGlobalA)
in both modules. Now while both names (initially) point to the same object, the names themselves are distinct - they live in different namespaces (one in ModuleA
and the other in ModuleY
), so when fA()
rebinds the name iGlobalA
- which is really ModuleA.iGlobalA
- only the name leaving in ModuleA
is impacted (so at this point both names point to different objects).
On the other hand when in ModuleY
you use the qualified name ModuleA.iGlobalA
, you only have one single name, so when this name is rebound (in ModuleA
) by fA()
you see the change in ModuleY
, because you are really checking the same name.
Note that if instead of rebiding a name you had tried the same thing with mutating a mutable object (ie appending to a list, updating a dict etc) you wouldn't have noticed any difference in behaviour:
# ModuleA
iGlobalA = []
def fA():
iGlobalA.append(1)
print( "MA: iGlobalA=", iGlobalA )
print( "Module A Initialised, iGlobalA=", iGlobalA )
What you need to understand here is mainly what Python "variables" really are, and also that Python has no real global namespace ("global" really means "module level").
I would have thought these differences between these import syntaxes should be more clearly stated in the documentation.
Possibly yes. Note that there actually some documentation about the whole thing, cf https://docs.python.org/3/reference/simple_stmts.html#import and https://docs.python.org/3/reference/import.html, but you do indeed have to understand what "defines a name in the local namespace" and "a reference to that value is stored in the local namespace" really imply.
It also means that someone designing a library module needs to specify how that module should be imported.
The problem here is either that the lib is badly designed (and if it uses globals that way it is badly designed indeed) or (at least) that the name you tried to access is not (or should not be) part of the lib's API.
Did I miss something in the documentation?
Possibly too, I'm afraid I'm far too used to how this work to remember how I first learned it
Is this behaviour the same across all platforms? Is this the intended behaviour that can be relied upon in future?
Yes and yes. This is part of the language's specifications actually and changing it would break almost all existing code.
Upvotes: 1