Reputation: 6801
>>> import math
>>> math.pi
3.141592653589793
>>> math.pi = 3
>>> math.pi
3
>>> import math
>>> math.pi
3
Initial question: Why can't I get math.pi
back?
I thought import
would import all the defined variables and functions to the current scope. And if a variable name already exists in current scope, then it would replace it.
Yes, it does replace it:
>>> pi = 3
>>> from math import *
>>> pi
3.141592653589793
Then I thought maybe the math.pi = 3
assignment actually changed the property in the math class
(or is it math module
?), which the import math
imported.
I was right:
>>> import math
>>> math.pi
3.141592653589793
>>> math.pi = 3
>>> from math import *
>>> pi
3
So, it seems that:
If you do import x
, then it imports x
as a class-like thing. And if you make changes to x.property, the change would persist in the module so that every time you import it again, it's a modified version.
Real question:
import
implemented this way? Why not let every import math
import a fresh, unmodified copy of math
? Why leave the imported math
open to change? math.pi
back after doing math.pi = 3
(except math.pi = 3.141592653589793
, of course)? import math
is preferred over from math import *
. But this behaviour leaves me worrying someone else might be modifying my imported module if I do it this way...How should I do the import
?Upvotes: 18
Views: 723
Reputation: 280963
Python only creates one copy of any given module. Importing a module repeatedly reuses the original. This is because if modules A and B imported C and D, which imported E and F, etc., C and D would get loaded twice, and E and F would get loaded 4 times, etc. With any but the most trivial of dependency graphs, you'd spend a few minutes loading redundant modules before running out of memory. Also, if A imported B and B imported A, you'd get stuck in a recursive loop and, again, run out of memory without doing anything useful.
The solution: Don't screw with the contents of other modules. If you do, that's an interpreter-wide change. There are occasionally situations where you'd want to do this, so Python lets you, but it's usually a bad idea.
Upvotes: 13
Reputation: 12208
The import behavior is intended to allow modules to have state. For example, a module that runs initialization code may have all sorts of different behaviors based on what happens at init time (a good example is the os module, which transparently loads different versions of the path submodule depending on what OS you're on). The usual behavior exists to allow lots of different code to access the module without re-running the initialization over and over. Moreover, modules function sort of like static classes in other languages - they can maintain state and are often used as an alternative to global variables: eg, you might use the locale module to set local culture variables (currency format, etc) -- calling locale.setlocale in one part of your code and local.getlocale in another is a nice alternative to making a global variable.
Your example, of course, points out the weakness. One of the classic python principes is
We're all adults here
The language does not provide much of the privacy management features you'd find in, say, Java or C# which let the author lock down the contents of a module or class. You can, if you're feeling malicious (or just suicidal) do exactly the sort of thing done in your example: change pi to equal 3, or turn a function into a variable, or all sorts of other nasty stuff. The language is not designed to make that hard -- it's up to coders to be responsible.
@Josh Lee's answer shows how to use reload, which is the correct way of refreshing a module to it's disk-based state. The wisdom of using reload depends mostly on how much init code is in the module, and also on the web of other modules which import or are imported by the module in question.
Upvotes: 6
Reputation: 177604
A module may be imported many times. An import
statement just loads the reference from sys.modules
. If the import
statement also reloaded the module from disk, it would be quite slow. Modifying a module like this is very unusual and is only done under rare, documented circumstances, so there’s no need to worry.
How to reload a module:
>>> import imp
>>> imp.reload(math)
<module 'math' (built-in)>
>>> math.pi
3.141592653589793
Upvotes: 10