Reputation: 7789
I have three modules:
constants
, which contains loaded configuration and other stuff in a classmain
, which initializes constants
when it is runuser
, which imports constants
and accesses its configuration.constants
module (simplified) looks like this:
class Constants:
def __init__(self, filename):
# Read values from INI file
config = self.read_inifile(filename)
self.somevalue = config['ex']['ex']
def read_inifile(self, filename):
# reads inifile
constants: Constants = None
def populate_constants(filename):
global constants
constants = Constants(filename)
A simple object which should hold the configuration, nothing groundbreaking.
The function populate_constants()
is called from main
on program startup.
Now the weirdness happens - when I import the constants
module from user
like this, the constants
is None
:
from toplevelpkg.constants import constants
print(constants)
None
However, if I import it like this, constants
is initialized as one would expect:
from toplevelpkg import constants
print(constants.constants)
<trumpet.constants.Constants object at 0x035AA130>
Why is that?
EDIT: in my code, the function attempting to read constants
is run asynchronously via await loop.run_in_executor(None, method_needing_import)
. Not sure if this may cause issues?
(and as a side-question, is it a good practice to have a configuration-holding object which parses the config file and provides it as its member variables?)
Upvotes: 0
Views: 71
Reputation: 77902
There's indeed a difference between
from mymodule import obj
(...)
do_something_with(obj)
and
import mymodule
(...)
do_something_with(mymodule.obj)
In the first case, it acts as :
import mymodule
obj = mymodule.obj
del mymodule
which means that at this point, in the current module, obj
is a "global" (which in python actually means 'module-level', not 'application-wide') name bound to whatever mymodule.obj
was when it was imported (in your case : None
). From then on, mymodule.obj
and the module-local obj
names live in different namespaces (the first one in mymodule
namespace, the second one in the current module namespace), and rebinding mymodule.obj
from anywhere won't change anything to what the current module's obj
is bound to. Actually, it's exactly as if you were doing this:
a = 2
b = a
a = 4
After the third statement, b
is obviously still bound to 2
- rebinding a
to 4
doesn't impact b
.
In the second case (import mymodule
) what get's bound in the importing module's namespace is the whole mymodule
object, so if mymodule.obj
gets rebound (from within mymodule
or anywhere else) the change will be visible in the importing module. In this case it's equivalent to
a = {"x": 2}
b = a
a["x"] = 4
In which case the change will be visible from b["x"]
as well since a
and b
are still bound to the very same object.
wrt/ your side question: yes, having some "config" object is quite a common pattern. You just may want to make sure you can build it "from scratch" too (I mean, not necessarily from a config file) to make unittesting easier.
Upvotes: 2