4

I'm working on a Python application in which there are two Singleton classes: App and Configuration.

The former seems straight forward, only ever instantiate one App instance; the latter seems controversial.

From the searches I've done, I need Configuration to be accessible from other modules that update the application's configuration file (and subsequently update the application). To do this, I've designed my class as a Singleton by controlling instantiation through its metaclasses __call__ method. To access the instance, in any module I do the following:

from app.config import Configuration

class Foo:

    def __init__(self, *args, **kwargs):
        self.config = Configuration()

Now, Foo is some feature beloning to App, I could've just as easily done:

from app import App

class Foo:

    def __init__(self, *args, **kwargs):
        self.config = App().configuration

Where App() returns the application Singleton and .configuration was an attribute where the Configuration was first instantiated. Further searching shows I could even use the app.config module as a Singleton, since Python only loads modules once. So regardless of defining a class as a Singleton or treating the module as a Singleton, the Singleton Pattern remains.

So what's wrong with either of these:

  1. Using the class Singleton
  2. Treating the module as a Singleton

In any case, I need a single configuration for the entire application. Creating multiple instances would lead to potential race conditions, so this seems like a smart way to handle it. Further, it seems extensible for features such as logging, right?

Christophe
  • 81,699
pstatix
  • 1,047

2 Answers2

7

There is a frequent confusion between having only one and must absolutely have only one.

While the latter certainly requires a singleton, the former only requires some discipline to create a configuration object and inject it where it is used.

If you think that your configuration object must absolutely exist only once, it might probably be because you use a global configuration. While such a shortcut has some convenience, it creates a heavy hidden dependency that is not necessarily desirable.

Robert Harvey
  • 200,592
Christophe
  • 81,699
3

In any case, I need a single configuration for the entire application.

You can use an external configuration file. You can instantiate multiple configuration objects, which may all simply read from and write to this very file. Therefore, you can obviously have multiple instances but a single set of configuration-related settings.

Creating multiple instances would lead to potential race conditions, so this seems like a smart way to handle it.

Singleton does not make any difference with respect to race conditions. You can access the same member of a singleton object from two threads as well. The object is the same, so you also have a race condition. Maybe you are confusing thread-safety with respect to initialization versus usage. You can make a Singleton thread-safe w.r.t. initialization in multiple colorful ways but this does not mean that this single-instance is also thread-safe to use.

Further, it seems extensible for features such as logging, right?

It is, indeed, but you will have a very hard time providing more than one logging implementation at a time, e.g. for separate parts of your application, because you cannot abstract a singleton anymore than that. You will have to expose everything through your singleton and things will get complicated fairly quickly.

So what's wrong with either of these:

  1. Using the class Singleton
  2. Treating the module as a Singleton

You cannot entirely infer the use of your classes just by looking at their public programming "interface" (API), i.e. you have hidden dependencies. A properly implemented singleton allows your classes to lie about their dependencies. Accessing the singleton from just about anywhere means you have to scan the source code meticulously if you are to find out what you are storing where. This makes your code a lot more fragile.

Then, when you suddenly decide that you need another configuration, changing the implementation of a Singleton comes at an overprice. You have to change all of your classes where you have used the singleton to employ a different implementation.

My personal suggestion in terms of architecture would be to avoid the Singleton or, at least, if you use app.config, hide it behind multiple classes, each of which exposes the very specific subset of the configuration settings that the expected consuming classes are going to use. You probably don't need access to the entire configuration in each and every consuming class, do you...?

Vector Zita
  • 2,502