Search code examples
pythondynamicsingletonglobal

Dynamically creating immutable constants that can be imported from everywhere?


In my python package I have an entry_point run.py file which takes the seed (e.g. 42) and the cuda device (e.g. "cuda:0") as command line argument.

Since both of these variables are used throughout the entire package at different places, I don't want to pass them as arguments from function to function. Hence, I did the following:

utils.py:

import random

import numpy as np
import torch


def set_device(device: str):
    global _DEVICE
    _DEVICE = torch.device(device)


def get_device() -> torch.device:
    return _DEVICE


def set_seed_number(seed: int):
    global _SEED
    _SEED = seed


def set_seeds():
    torch.manual_seed(_SEED)
    random.seed(_SEED)
    np.random.seed(_SEED)

And then within run.py I set these variables once by calling:

from package.utils import set_device, set_seed_number

...

set_device(device)
set_seed_number(seed=seed)

Now I can import and call the get_device()and set_seeds method from anywhere in my package and I don't have to pass these variables as arguments.

So far this approach works fine, but after reading that using globals in python is strongly discouraged I am wondering if there is a more pythonic way of achieving the above discribed goal?

I already thought of having a dedicated Singleton class, which dynamically would instantiate those constants but I am not exactly sure if and how that would work and if it would be considered more "pythonic" after all.

Thanks already for your answers and maybe you can point me to some patterns that seem applicable in this situation. I can only guess that I am not the first one trying to achieve the above discribed goal.


Solution

  • I can't honestly see a problem with global if it is used sparingly and only when there is a strong reason to do so. (I think the strong discouragement aganst global is because it is often abused.)

    But as regards your proposed custom class, there is no need to instantiate it -- you can just set class variables.

    main.py

    import settings
    
    settings.set_foo(3)
    print(settings.Settings.foo)
    

    settings.py

    class Settings:
        pass
    
    def set_foo(x):
        Settings.foo = x
    

    This is no different in principle from putting your data items inside some other mutable collection e.g. a dictionary and then setting them inside functions in the module that defines it (or another that imports it).

    main.py

    import settings
    
    settings.set_foo(3)
    print(settings.settings['foo'])
    

    settings.py

    settings = {}
    
    def set_foo(x):
        settings['foo'] = x