I understood that
import XXX
print(XXX.a)
Y
from XXX import a, b, c
print(a)
they were the same, and choosing one over the other was a matter of personal preference.
The thing is, I have this module servicio.py
that contains the mockup of a service that reads some data and keeps it in the variable var
.
Every time the service is called, var
it should be updated, which I simulate by adding 100 to it on each pass.
service.py
var = 1
def cambia():
global var
var += 100
print(f"cambia var = {var}")
From another module I want to call the service and then retrieve the value by directly accessing the variable var
.
Alternative 1
import servicio
print(id(servicio.var), servicio.var)
servicio.cambia()
print(id(servicio.var), servicio.var)
which produces:
9788992 1
cambia var = 101
9792192 101
Alternative 2
from servicio import var, cambia
print(id(var), var)
cambia()
print(id(var), var)
which produces:
9788992 1
cambia var = 101
9788992 1
Can someone explain the difference to me?
import
As you've already found out "the hard way" those are not equivalent syntaxes . But what is the difference exactly?what it does is load the file
servicio.py
into memory, execute it, and create in the local namespace the symbolservicio
, which acts as if it were an object, in the sense that it supports the "dot" syntax to access its internal symbols, so so you can putservicio.var
,servicio.cambia
, etc. All symbols that youservicio.py
define at theglobal()
global level (operates at the module level), are accessible from the code that imports them through the "dot" notation.instead when you do
what happens is that, as before, it is loaded
servicio.py
into memory, executed, and the symbol is created in the local namespacevar
.Simplifying a lot, it is as if it had been done:
So we now have two
var
different symbols, with different visibility:var
that is listed if you doglobals()
insideservicio.py
and that was created as a result of the linevar=1
in that file.var
that is listed if you doglobals()
insidemain.py
, and that was created as a result of thefrom servicio import var
. This starts initialized with the value 1. I guess you won't be too surprised to discover that if we didvar = 2
that , it wouldmain.py
only change the value of the local symbol, without affecting the symbol described in point 1.When the function
cambia()
doesvar+=100
, it is actually changing the value of symbol 1 of the two explained above. The value of symbol 2 is not affected, as it is in another namespace.When you do the you do
import servicio
n't see these side effects because you are always referring to the 1 symbol, by having to use theservicio.valor
.Note : Looking
id()
at the variables does not help much here, and rather it is confusing, because basicallyid()
what it returns is a kind of hash of the value it contains. It actually returns the memory address where the corresponding data is, but not the address of the variable , which is probably what you wanted to find out. There is no way to get the address of the variable in python, and the address of the data doesn't help because it obviously changes if you change the value of the variable.It's not exactly the same. The difference between
from a import b
eimport a
(or evenimport a.b
) lies in what it does at the reference level.import a
what it does is import the modulea
and create a reference in the current namespacefrom a import b
import the modulea
and create a reference tob
What happens when we do
from a import b, c
?The interpreter imports
a
and creates two different references tob
yac
, generating situations like the ones you describe.Although they are objects from the same module, they are referenced separately.
Symbolically, what you are doing in
alternativa 1
corresponds to inheriting the characteristics of a "parent node" let 's call itPadreA
(a class object)... When you subsequently use:From the "nodes" you would be doing something like:
Note that if it
PadreA
stored some kind of memory such that it changes with each execution, like a counter (at the end I mention that it is more likely in this case), it would be different than the second alternative:Because you would only be calling parent nodeA twice and not three times.
Another difference, as @abulafia mentions, is that with the first alternative you are inheriting all the methods of the class... so it
PadreA
would haveN
children, where N is the number of methods of the module.Instead, alternative two;
PadreB
It would only have the direct child of the method you called. It would only have1
method (notN
like in the first alternative).* Clarification: These are conclusions that I have drawn from my personal use of classes and modules, I have no proof but I think it makes a lot of sense.
Among other things, being redundant, trying with
servicio.py
like:The output is replicated, using a debugger the objects in memory are checked (alternative 1):
Which contains a single instance of an object, two different methods are called from the same instance.
and alternative 2:
In summary:
*What actually happens is that alternative 2, when executed in different instances, have no relationship, and is unable to modify the variable
var
, it turns out to have the same oneid
since it has not changed