Kowalski Paweł
Kowalski Paweł

Reputation: 626

Using module as a singleton in Python - is that ok?

I've got a really complex singleton object. I've decided to modify it, so it'll be a separate module with module--wide global variables that would store data.

Are there some pitfalls of this approach? I just feel, like that's a little bit hacky, and that there may be some problems I cannot see now. Maybe someone did this or have some opinion :) Thanks in advance for help. Regards.

// Minimal, Complete, and Verifiable example:

"""

This is __init__.py of the module, that could be used as a singleton:

I need to set and get value of IMPORTANT_VARIABLE from different places in my code.

Folder structure:
--singleton_module
 |
 -__init__.py

Example of usage:
import singleton_module as my_singleton
my_singleton.set_important_variable(3)
print(my_singleton.get_important_variable())

"""

IMPORTANT_VARIABLE = 0

def set_important_variable(value):
    global IMPORTANT_VARIABLE
    IMPORTANT_VARIABLE = value

def get_important_variable():
    return IMPORTANT_VARIABLE

Upvotes: 17

Views: 17870

Answers (1)

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

Technically, Python modules ARE singletons, so from this point of view there's no particular issue (except the usual issues with singletons that is) with your code. I'd just spell the varibale in all_lower (ALL_UPPER denotes a pseudo-constant) and prefix it with either a single ("protected") or double ("really private") leading underscore to make clear it's not part of the public API (standard Python naming convention).

Now whether singletons are a good idea is another debate but that's not the point here...

e.g that in one potential situation I may lost data, or that module could be imported in different places of code two times, so it would not be a singleton if imported inside scope of function or something like that.

A module is only instanciated once per process (the first time it's imported), then subsquent imports will directly get if from sys.modules. The only case where you could have two distinct instances of the same module is when the module is imported by two different path, which can only happens if you have a somewhat broken sys.path ie something like this:

src/
  foo/
    __init.py
    bar/
      __init__.py
      baaz/
         __init__.py
         mymodule.py

with both "src" and "foo" in sys.path, then importing mymodule once as from foo.bar.baaz import mymodule and a second time as from bar.baaz import mymodule

Needless to say that it's a degenerate case, but it can happens and lead to hard to diagnose bugs. Note that when you have this case, you do have quite a few other things that breaks, like identity testing anything from mymodule.

Also, I am not sure how would using object instead of module increase security

It doesn't.

And I am just asking, if that's not a bad practice, maybe someone did this and found some problems. This is probably not a popular pattern

Well, quite on the contrary you'll often find advises on using modules as singletons instead of using classes with only staticmethods, classmethods and class attributes (another way of implementing a singleton in Python). This most often concerns stateless classes used as namespaces while your example does have a state, but this doesn't make much practical difference.

Now what you won't get are all the nice OO features like computed attributes, inheritance, magicmethods etc, but I assume you already understood this.

As far as I'm concerned, depending on the context, I might rather use a plain class but only expose one single instance of the class as the module's API ie:

# mymodule.py

__all__ = ["mysingleton"]

class __MySingletonLike(object):
    def __init__(self):
        self._variable = 42

    @property
    def variable(self):
        return self._variable

    @variable.setter
    def variable(self, value):
        check_value(value) # imaginary validation
        self._variable = value


mysingleton = __MySingleton()

but that's only when I have special concerns about the class (implementation reuse, proper testability, other special features requiring a class etc).

Upvotes: 32

Related Questions