Reputation: 351
For the life of me I am finding me python transition to be extremely frustrating. One of the things I am attempting at doing is to initialize a single instance of a class from a configuration dictionary, then access that class in other modules.
The problems I am facing / the approaches I have taken are not working out and am hoping someone could steer in the right 'pythonic' approach.
First off my app can be initialized as part of a twistd plugin, or as a standalone script.
import resource
class App(object):
_config = None
_someotherobject = None
def __init__(self, config):
self._config = config
....
def main():
global myapp
myapp = App({}) # Could use help here, how to pass config to it
myapp = None #Global doesnt work as I expect, it doesnt modify this instance, stays as None
if __name__ == '__main__':
main()
#-----------resource.py
class Foo(object):
def foo(self):
app.myapp.somefunction() #NoneType object has no attribute
I have verified the app object is created before the code in the other module is kicked off. I just can't figure out why the 'global' instance in the module doesn't actually do what I expect, also confused as to how to reference the instance from another module.
----- Edit ------
To clarify a couple points, the script is called with python app.py
app.py references a module called resources.py
which is a bunch of class definitions. In some of the classes, when executed, they reference the 'singleton' instance of app.myapp.
Upvotes: 2
Views: 8544
Reputation: 365657
Your biggest problem is the one mentioned by sotapme. If you don't actually run app.py
as your top-level script, you don't have any code calling app.main()
, and therefore nothing initializing the global.
Beyond that, "how to reference the instance from another module" is very easy, if you know one simple thing:
In Python, globals are namespaced.
To put it more concretely, in your other modules, just import app
, then access the global as app.myapp
.
Using the __name__ == '__main__'
trick, as sotapme explained, means that you can import app
as many times as you want without it running the main()
function every time.
In particular, when you run this:
python app.py
The Python interpreter will load app.py
with its __name__
set to '__main__'
, so the if
statement will trigger, which will cause the (module-level) global variable myapp
to get set to App({})
.
Now, when resource.py
does an import app
, its __name__
will be set to app
, so the if
statement will not trigger, so you will construct a new App
and replace the global. From the code in resource.py
, you can use app.myapp
, and you will be accessing the same object that code in app.py
sees as myapp
.
You also ask for help to pass config to the App
constructor. I'm not sure what your problem is here. You're passing an empty dict
as a config. If you have a different dict
to pass, just use it. Instead of this:
myapp = App({}) # Could use help here, how to pass config to it
do this:
myapp = App(configdict)
If your problem is knowing how to get that configdict
, that depends on where the information comes from.
If you're trying to parse a user-editable config file, the configparser
module works fine for traditional .ini-style files, and the docs have links that explain how to handle some other popular formats.
If you're trying to build up config information from a command line, see argparse
.
If you want to allow environment variables to interact with either of the above (e.g., a MYAPP_CONFIG
might tell your configparser
code to load a different config file than normal, or a MYAPP_CACHE_DIR
might provide a different default for the --cachedir
command-line argument for argparse
), you get the values in os.environ
, but have to write your own code to do anything with them.
Upvotes: 0
Reputation: 483
To clean up a little bit, here is an example:
class App:
_config = None
_someotherobject = None
def __init__(self, config):
self._config = config
def main():
myapp = App(config) # pass a config object to App
if __name__ =='__main__':main()
From another app you will have to do the following:
from first_app import App
myapp = App(config)
Upvotes: 0
Reputation: 4903
Your main
will only ever be called as a standalone script not when imported from another module.
if __name__ == '__main__':
main()
Is the trick to make your modules runnable from the command line.
To prove it
import app
app.main()
Then run your piece of code.
Once you've initialised it app
becomes like a singleton, any other module importing it will get that initialised version.
I had a similar problem where I didn't want to access app
but wanted modules to be able to say app = MyApp()
and still share the same data ( I forget why I wanted it but it might have had to do with wanting it initialised on first use)
I ended up using a Borg instead of a Singleton.
Upvotes: 3