best way to share an instance of a class among modules?

C

CM

I have recently moved all my SQLite3 database-related functions into a
class, DatabaseAccess, that lives in a "utilities" module. When the
application loads, the namespace of the instance of the class is
populated with two different cursors for two different databases the
whole application needs to use. One of those cursors always refers to
a connection to the same database, but the other cursor can change
which database it refers to; in this regard, the namespace of the
DatabaseAccess class instance holds a "state" (which is which database
the one cursor currently refers to)

I have a number of modules that all need to use those two cursors.
However, if I import the utilities module and create a new instance of
the DatabasesAccess() in each of all the various modules, obviously
each new instance doesn't have the same namespace as the first
instance, and so doesn't have the *same* two cursors.

I can think of a few ways to pass the DatabaseAcess instance around to
the various modules, but all of them seem like repeating myself and
clunky. What's the "best" (most Pythonic, simplest) way for a number
of modules to all share the same instance of my DatabaseAccess class?

Thanks,
Che
 
T

Terry Reedy

I have recently moved all my SQLite3 database-related functions into a
class, DatabaseAccess, that lives in a "utilities" module. When the
application loads, the namespace of the instance of the class is
populated with two different cursors for two different databases the
whole application needs to use. One of those cursors always refers to
a connection to the same database, but the other cursor can change
which database it refers to; in this regard, the namespace of the
DatabaseAccess class instance holds a "state" (which is which database
the one cursor currently refers to)

I have a number of modules that all need to use those two cursors.
However, if I import the utilities module and create a new instance of
the DatabasesAccess() in each of all the various modules, obviously
each new instance doesn't have the same namespace as the first
instance, and so doesn't have the *same* two cursors.

I can think of a few ways to pass the DatabaseAcess instance around to
the various modules, but all of them seem like repeating myself and
clunky. What's the "best" (most Pythonic, simplest) way for a number
of modules to all share the same instance of my DatabaseAccess class?

Attach the instance of the class to the utilities module that clients
import. It does not matter if the code is in the utilities module itself
or in one of the importing modules (possibly the first).

# in module
shared_cursor =

# in importer
import utilities
utilities.shared_cursor = DatabaseAccess(args)

Module attributes are mutable and can be set from elsewhere.
 
C

CM

Attach the instance of the class to the utilities module that clients
import. It does not matter if the code is in the utilities module itself
or in one of the importing modules (possibly the first).

# in module
shared_cursor =

# in importer
import utilities
utilities.shared_cursor = DatabaseAccess(args)

Module attributes are mutable and can be set from elsewhere.

Thank you. But, I'm sorry, I'm not following this enough to get it to
work. Shouldn't it be a little more like this:

# in utilities module
shared_cursor = DatabaseAccess_instance #but how? see my question
below...

# in importer
import utilities
self.shared_cursor = utilities.shared_cursor ("self" is here to make
cursor available to all functions in importer

My only problem, then, is I create the shared_cursor from within a
function within the instance of DatabaseAccess(). How then do I pass
it from within the function's namespace to the module's namespace so
that I can do that first line?
 
M

Michael Torrie

Thank you. But, I'm sorry, I'm not following this enough to get it to
work. Shouldn't it be a little more like this:

No, not exactly.
# in utilities module
shared_cursor = DatabaseAccess_instance #but how? see my question
below...

How what?
# in importer
import utilities
self.shared_cursor = utilities.shared_cursor ("self" is here to make
cursor available to all functions in importer

Umm no. For one you're using self incorrectly. For two, it already is
visible to all functions in the module. You just have to refer to it as
"utilities.shared_cursor."
My only problem, then, is I create the shared_cursor from within a
function within the instance of DatabaseAccess(). How then do I pass
it from within the function's namespace to the module's namespace so
that I can do that first line?

Every function in a module has access to the module's global namespace.
And your shared_cursor is there, inside of the utilities reference,
since utilities was imported into your module, "importer."
 
M

Michael Torrie

Every function in a module has access to the module's global namespace.
And your shared_cursor is there, inside of the utilities reference,
since utilities was imported into your module, "importer."

Just to be clear:

mod1.py:
a=5

------------------------
mod2.py:
import mod1

def testfunc1():
print mod1.a

def testfunc2():
mod1.a = 6

-------------------------
main.py:
import mod1
import mod2

mod2.testfunc1()
mod2.testfunc2()
mod2.testfunc1()

-----------------------

The main.py program will print out "5" and "6."

If I understand your original question, this example will demonstrate
it. But I could have understood wrong!
 
C

c

No, not exactly.




How what?


Umm no.  For one you're using self incorrectly.  For two, it already is
visible to all functions in the module.  You just have to refer to it as
"utilities.shared_cursor."

I was using self correctly, I think; but I should have said that the
code in the importing module would be within a class, so self there
refers to that class. But that's a side point.

I agree that utilities.shared_cursor is visible within the importing
module. But the problem below remains for me...
Every function in a module has access to the module's global namespace.
 And your shared_cursor is there, inside of the utilities reference,
since utilities was imported into your module, "importer."

But the function in the module is also within a *class* so I don't
think the function does have access to the module's global namespace.
Here's the hierarchy:

-- Module namespace....
---- class namespace (DatabaseAccess is the name of the class)
---- function namespace
This is where the cursor is created. How do I get it
into the module namespace?

Che
 
C

CM

I was using self correctly, I think; but I should have said that the
code in the importing module would be within a class, so self there
refers to that class. But that's a side point.

I agree that utilities.shared_cursor is visible within the importing
module. But the problem below remains for me...
Every function in a module has access to the module's global namespace.
And your shared_cursor is there, inside of the utilities reference,
since utilities was imported into your module, "importer."

But the function in the module is also within a *class* so I don't
think the function does have access to the module's global namespace.
Here's the hierarchy:

-- Module namespace....
---- class namespace (DatabaseAccess is the name of the class)
---- function namespace
This is where the cursor is created. How do I get it
into the module namespace?
 
E

Ethan Furman

But the function in the module is also within a *class* so I don't
think the function does have access to the module's global namespace.
Here's the hierarchy:

-- Module namespace....
---- class namespace (DatabaseAccess is the name of the class)
---- function namespace
This is where the cursor is created. How do I get it
into the module namespace?

In the class namespace you have something like:

self.shared_cursor = ...

to get that into the module namespace instead, just remove the 'self':

shared_cursor = ...
 
M

Michael Torrie

But the function in the module is also within a *class* so I don't
think the function does have access to the module's global namespace.
Here's the hierarchy:

-- Module namespace....
---- class namespace (DatabaseAccess is the name of the class)
---- function namespace
This is where the cursor is created. How do I get it
into the module namespace?

Oh I see. You're trying to set a variable in the current module's
global namespace. There's two ways to do this, the first one is cleaner
and preferred:

1. Have a function in DatabaseAccess return the cursor then then set it
to a name in code that runs at the module level.

2. use the "global" keyword in the method:

def mymethod(self):
global blah

blah = something


Then your module has has blah in it's global method now. If you want to
stick a variable in another method's global namespace, as long as you've
imported it you just go:

module.newvariable = something
 
E

Ethan Furman

In the class namespace you have something like:

self.shared_cursor = ...

to get that into the module namespace instead, just remove the 'self':

shared_cursor = ...

Correction:

As Michael Torrie pointed out, the 'global' keyword is needed:

global shared_cursor
shared_cursor = ...
 
R

Rick Johnson

As Michael Torrie pointed out, the 'global' keyword is needed:

Wrong. The global keyword is in fact NOT needed and something i consider to be another wart of the language (PyWart on this subject coming soon!).

Now, whilst Micheal's advice is valid python code, i would strongly suggest against encouraging people to use the global keyword when they could simply declare the variable beforehand. I know what you are thinking:

""" But python does not /require/ us to declare global variables if we use the global keyword"""

Yes, i know. But i just hate to have variables created "magically" in the middle of execution! ESPECIALLY if these variables are going to be accessed from another module(s). *sniff-sniff* does anyone smell what i smell?

Listen. I want to know every variable and every CONSTANT that will exist in the module namespace, and i want to know these facts BEFORE i read anything else. So please declare them at the top of each module.


Hierarchy of module code structure:

0. shebang lines
1. stdlib import statements
2. 3rd party and dependency import statements
3. CONSTANTS
4. $globalVariables
5. module functions
6. module classes (bka: Object Definitions)
7. Testing Area

So if you want to use "global variables" , (bka: Module level variables), then simply declare them with a None value like this:

globalVariable = None

But don't pat yourself on the back just yet! Place a little comment so the reader will understand that this variable is accessed from ANOTHER namespace.

sharedCursor = None # Set by "Class.blah"; Whored out to X,Y and Z!

In this manner i will not be blindsided by proper use of poor language features.
 
R

Rick Johnson

As Michael Torrie pointed out, the 'global' keyword is needed:

Wrong. The global keyword is in fact NOT needed and something i consider to be another wart of the language (PyWart on this subject coming soon!).

Now, whilst Micheal's advice is valid python code, i would strongly suggest against encouraging people to use the global keyword when they could simply declare the variable beforehand. I know what you are thinking:

""" But python does not /require/ us to declare global variables if we use the global keyword"""

Yes, i know. But i just hate to have variables created "magically" in the middle of execution! ESPECIALLY if these variables are going to be accessed from another module(s). *sniff-sniff* does anyone smell what i smell?

Listen. I want to know every variable and every CONSTANT that will exist in the module namespace, and i want to know these facts BEFORE i read anything else. So please declare them at the top of each module.


Hierarchy of module code structure:

0. shebang lines
1. stdlib import statements
2. 3rd party and dependency import statements
3. CONSTANTS
4. $globalVariables
5. module functions
6. module classes (bka: Object Definitions)
7. Testing Area

So if you want to use "global variables" , (bka: Module level variables), then simply declare them with a None value like this:

globalVariable = None

But don't pat yourself on the back just yet! Place a little comment so the reader will understand that this variable is accessed from ANOTHER namespace.

sharedCursor = None # Set by "Class.blah"; Whored out to X,Y and Z!

In this manner i will not be blindsided by proper use of poor language features.
 
M

Michael Torrie

Wrong. The global keyword is in fact NOT needed and something i
consider to be another wart of the language (PyWart on this subject
coming soon!).

Actually in both Python 2 and 3, the global keyword is, in fact needed.
Otherwise the name binding in the function hides the global name. So
no matter if you declare the variable in the module itself (which yes I
agree you should always do!), you need the global keyword in the
function if you want to overwrite the name in the module's global namespace.

Observe the following code which I tested on both python2 and python3:
--------
from __future__ import print_function

my_global_var = 4

def test_func():
#global my_global_var
my_global_var = 6

if __name__ == "__main__":
print(my_global_var)
test_func()
print(my_global_var)

---------

Without the global statement, you get 4 printed out both times. At
least on the python interpreters I have handy. With the global
statement uncommented, I get 4 and 6.

Do you understand the difference between binding a name to an object and
variable assignment? Because that's the reason we see this behavior.
This would be a gotcha in this case. Not a wart, but simply something
to be aware of.
 
M

Michael Torrie

So if you want to use "global variables" , (bka: Module level
variables), then simply declare them with a None value like this:

globalVariable = None

This is a nice convention, but at best it's just a helpful notation that
helps a programmer know something useful may likely be bound to this
name in the future.

The '=' sign in Python isn't an assignment operator, at least in this
case; it's a binding operator. Thus any future line that sets
globalVariable to something isn't assigning a value to this
already-declared variable. Instead it's binding the name globalValue to
a new object, and overwriting the name in the module dictionary. That's
of course why the global keyword is required, so that Python will use
the module dictionary instead of the local scope one.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top