Why do we need a constructor for a singleton?

49 Views Asked by At

Why can't we just create static methods and attributes to use globally in our project? Using only class attributes without any object attributes also wouldn't be part of the singleton pattern, would there be any problem in using it this way? It's only for cases like unit tests where we need custom constructors? Example:

class GlobalInfo:
    _global_data = {}
    _lock = threading.Lock()

    @staticmethod
    def set_data(key, value):
        with GlobalInfo._lock:
            GlobalInfo._global_data[key] = value

    @staticmethod
    def get_data(key):
        with GlobalInfo._lock:
            return GlobalInfo._global_data.get(key)

I need something with singleton-like behavior to use in a project where more than one thread needs access to the same information. After reading a bit, I saw that people always override the __new__ method, etc. I would like to know if I really need to do that, and if not, for my specific case, would I be violating the singleton pattern?

1

There are 1 best solutions below

0
jsbueno On

No, there is no need to that in most cases, and you are right.

In some cases, however, the singleton needs to be initalized at runtime - maybe to make available in Python code the effect of some configuration parameter, or create some other process-wide resource.

In this case these recipes are valid - but not needed.

A pattern I like most, whenever I need a singleton like this (and usually, I prefer it than marking methods as static even if no initialization is needed), is to simply create an instance of the singleton class at module level - so that instance will be the object being imported and used through the project (and not its class).

Like in:

class _MyConf:
    def __init__(self):
        # code that sets some attributes to be used process-wide
    def  method(self):
        # plain instance method
        ...

MyConf = _MyConf()

Followed by documenting that MyConf should be used.

And if, for uniformity purposes, there is the need, or desire for the singleton to be called with the instance creation syntax, like proposed in the comment by @Barmar:

The idea is that the callers should be able to use singletons just like any other objects, the fact that it's a singleton is an implementation detail. But your design requires them to create the instance differently, foo = GlobalInfo instead of foo = GlobalInfo()

So - if that is desired, I simply include a __call__ method returning self in the class:

class _MyConf:
    def __init__(self):
        # code that sets some attributes to be used process-wide
    def  method(self):
        # plain instance method
        ...
    def __call__(self):
        return self

MyConf = _MyConf()

But I disagree with the reasoning that "the idea is to maintain it callable", however - Python's own builtin singletons like None, True and False do not need to be "instantiated", and they work very well. It may be a good fit in some places, though.

Not that when dealing with static typing, the __call__ approach above won't work - in that case, the recipes using __new__ might be a good approach, if one really wants the Singleton class to fake instantiation when it is to be used.