Which is the best way to define module variables in Python?

Question:

I am developing a project with FastAPI and I have to connect to a database. I have decided to create a module with database-related functionality, like managing the connection or getting the table’s metadata (I am using SQLAlchemy). I want to retrieve this information without recalculating it or duplicating the objects. This can be done in many ways. The one I have chosen due to its simplicity and clarity is to store the values in variables that get the data from functions:

db.py:

def get_conn():
    [...]
    return conn

def get_metadata(conn):
    [...]
    return metadata

conn = get_conn()
meta = get_metadata()

other_file.py:

from db import conn, meta
[...]

This works fine, but I am not sure if it is the best way to do it. Another option is to use the @cache decorator on the functions, remove the variables, and call the functions instead of accessing the variables. I would like to know if there is a better or standard way to solve this. Thanks!

EDIT: I am not asking for an opinion. I want to know which are my options and their tradeoffs. Some of the replies and comments have been really useful so far since they have pointed out problems with my implementation or have proposed interesting alternatives.

Asked By: edoelas

||

Answers:

This can be problematic because the module is executed at applicatoin starting time, in no particular order.

If you encapsulate these as properties in a class, it becomes trivial to create the live objects the first time they are needed, and cache the results – and even, for the case of connections, to check if they are still alive or need to be recreated at that point in time.

This is a case for a "singleton": you create a class with the needed properties, and all your app want a single instance of that – the major difference to defining the variables directly as you suggest is that the property mechanism can them be leveraged for lazy creation of the objects. otherwise you will note that the assignment of the singleton instance itself follows the same pattern you are currently using.


class _Config:
    _coon = None
    _metadata = None

    def get_conn():
        [...]
        return conn

    def get_metadata(conn):
        [...]
        return metadata

    @property
    def conn(self):
        if self._conn is None:
            self._conn = self.get_conn()
        return self.conn
    
    @property
    def metadata(self):
        if self._metada is None:
            self._metadata = self.get_metadata()
        return self._metadata
    


# Bellow, the single instance of the class you will
# ever need is created: you won't need to look at the class 
# in the application lifetime again. 
config = _Config()   


# and in other modules, you simply import the name `config` above and do:
# "config.conn" or  "config.metada" to get to the desired objects. 


Answered By: jsbueno
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.