12

I am reorganizing my code and therefore creating new namespaces. I'm changing "static" classes (classes with @staticmethod in each method) for modules. This is the way to go, right?

The problem is that I have doubts on how to share the resources between these modules.

Let's say I had a module from which I was doing all connections to database, and of course all classes/methods were sharing the variable which stored the DB cursor (I'm using SQLite). Now, in different modules, they also have to share the cursor.

Graphical representation of dependences

So, my ideas:

  • Declare the global variable in each module. But globals are evil, eat children and steal our jobs. So I don't know if this is the way to go.

    '''Sub Module 1'''
    
    global database_cursor
    
  • Import the "father" database_module with the original database_cursor and use something like this:

    '''Sub Module 1'''
    
    db_cursor = database_module.database_cursor
    

This second looks fine in this case, but I think in many cases will lead to recursive imports, which I guess it´s something to avoid.

Bakuriu
  • 85,459
  • 18
  • 168
  • 202
bgusach
  • 13,019
  • 10
  • 44
  • 61
  • 1
    I think you may be [overengineering](http://en.wikipedia.org/wiki/Overengineering). If the functions needs a cursor to work on just add a parameter `cursor` to the functions and you are done. The code that calls all the function will create a single local variable containing the cursor and passing it around to all the functions. Also, I'd say a single module for the database management is enough. If you find that you need more organization then you are better using something like `SQLAlchemy`. – Bakuriu Nov 30 '12 at 10:10
  • 1
    I don't think this is over engineering. It is perfectly legit to share things like database connections to avoid unnecessary reinitialization. I however do think it is wise to look into connection pooling for this. Otherwise you get probably problems with the connection still in use from a previous call. – RickyA Nov 30 '12 at 10:36

1 Answers1

19

Your second method is the way to go. Python imports are singleton by nature. When a module is imported multiple times it is only executed the first time. Subsequent imports fetch the module object instance from the globals. More on that here.

shared.py:

class Shared:
    def __init__(self):
        print("Init shared")

    def do_stuff(self, from_mod):
        print("Do stuff from {0}. I am instance {1}".format(from_mod, self))

shared = Shared()

foo.py

import shared

shared.shared.do_stuff("foo")

bar.py

import foo
import shared

shared.shared.do_stuff("bar")

If we execute bar.py we get:

>>> Init shared
>>> Do stuff from foo. I am instance <shared.Shared instance at 0x10046df38>
>>> Do stuff from bar. I am instance <shared.Shared instance at 0x10046df38>

So in your case you can reference database_module from anywhere you want and it gets initialized only once, therefore effectively sharing your connection.

RickyA
  • 13,357
  • 5
  • 63
  • 89
  • If you execute foo.py and bar.py separately in a sequence, does that mean the __init__ method executes twice ? Im trying to understand if the singleton behavior applies when same module is imported multiple within a module or across modules? – Naveen Apr 06 '18 at 16:32
  • Note that that in the python docs https://docs.python.org/3/reference/simple_stmts.html#grammar-token-import-stmt this process is not written up too explicitly; the key statement is "initializing it if necessary", where "if necessary" is to be interpreted as "has not been previously initialized". – jarm Dec 29 '20 at 07:58