pythondictionarymodulepython-module

How to create a module of constants accessible directly and through a dictionary


I want to create a module called 'constants.py' that contain all the constants specific to my system.

To offer flexibility I would like to be able to access the constants directly like this:

constants.power_1
constants.power_2
constants.data_rate_1
constants.data_rate_2
...

because it is short, or access the constants through a dictionary like this:

constants['system_1']['power']
constants['system_2']['power']
constants['system_1']['data_rate']
constants['system_2']['data_rate']
...

because it is useful when sometimes I use a constant inside a loop like this:

systems = ['system_1', 'system_2']
for system in systems:
    power = constants[system]['power']
    var_2 = var_1 * power

My problem is that I cannot use both methods at the same time.

What I can do:

Option 1. Forget about accessing a constant like constants.power_1, and have only a dictionary like constants['power_1']. So I declare them like this in 'constants.py':

constants['power_1'] = 10
constants['system_1']['power'] = constants['power_1']

and use them like this:

from constants import *
var_2 = var_1 * constants['power_1']
var_2 = var_1 * constants['system_1']['power']

Cons:

Option 2. Use an intermediate name for my dictionary. So I declare them like this in 'constants.py':

power_1 = 10
varname['system 1']['power'] = power_1

and use them like this:

import constants
var_2 = var_1 * constants.power_1
var_2 = var_1 * constants.varname['system 1']['power']

Cons:

Do you see another way to solve this?


Solution

  • To be honest, if you wanted to do this I would recommend creating a class-based structure, that will provide a dictionary-like structure for hierarchical constants, and a flat namespace for individual constants.

    Constants.py could look something like

    class Constants:
    def __init__(self):
        # flat - you can also maybe load this from a JSON so you don't touch this often
        self._flat_constants = {
            "power_1": 10,
            "power_2": 20,
            "data_rate_1": 100,
            "data_rate_2": 200,
        }
        
        # heirachy
        self._nested_constants = {
            "system_1": {
                "power": self._flat_constants["power_1"],
                "data_rate": self._flat_constants["data_rate_1"],
            },
            "system_2": {
                "power": self._flat_constants["power_2"],
                "data_rate": self._flat_constants["data_rate_2"],
            },
        }
    
    def __getitem__(self, key):
        # First look through the flat ones
        if key in self._flat_constants:
            return self._flat_constants[key]
        
        # Then check nested ones
        if key in self._nested_constants:
            return self._nested_constants[key]
        
        raise KeyError(f"Constant '{key}' not found.")
    
    def __getattr__(self, name):
        # Allow direct access to flat constants
        if name in self._flat_constants:
            return self._flat_constants[name]
        
        raise AttributeError(f"'Constants' object has no attribute '{name}'")
    

    Then you can just use it by initializing wherever you plan to, knowing that you can use the dot to access the flat constants (constants.power_1), and the ['system_1]['power'] to access the nested ones.

    Tip: If you eventually decide to load from a JSON, you should add some logic to raise an exception if duplicates are found in the keys (duplicates are allowed in JSON).