A singleton is an object for which there is only one instance in the entire system. Singleton is a creational design pattern.
Examples of potential singletons include:
- A logging system. Various parts of the system might need to write log messages as the system executes. These will normally need to be written out to the same file, so you will normally only want a single logger object that the whole system uses.
- A database connection. The system might use a single database for storing all of its data, and you might need to access that database connection from many places in your code.
- A device driver. A manufacturing system, for example, might be controlling a robotic arm. Any parts of the software that generate control instructions for the device will need to send those instructions to the device driver.
Generally, a singleton can be useful wherever you have a single object that needs to be accessed from many different places in the software.
The singleton pattern affects the way the object is constructed.
Normally, if you have a class
A, then each time you call the constructor, it will return a new object of type
With a singleton class
- The first time you call the constructor, it will return a new object
- If you call the constructor again, one or more times, it will always return the same object
This means that there will only ever be one
S object created, and it will be created the first time your code calls the constructor.
Taking the example of the logger, then each time your code needs a logger, it will simply create one:
logger = Logger() logger.error(...)
Even if this code is deep down in some low-level class, you can just create a
Logger whenever you need one. Due to the singleton pattern, a new
Logger will only be created the very first time you call the constructor, wherever in your code that might be. Every time after that, the constructor will simply return the original
Logger object. There will only ever be one
There are a couple of alternatives, that don't involve using the singleton pattern.
The first is just to create a global
Logger object in a top-level module of your code (let's call it the
LOGGER = Logger()
You can then import this object and use it anywhere in our code:
from globals import LOGGER LOGGER.error(...)
This isn't completely unreasonable in the context of a "special case" like logging. The main arguments against this are:
- The logging object is baked into all your functions. If you want to unit test a class, you can't isolate it from the logging module.
- Global variables are generally a bad thing. If you do it for logging because it is a special case, the bad practice might proliferate into other areas.
The other alternative is to implement
Logger as a normal class. In your code, you would then create a single
Logger object that you could pass into every other object that needs it.
This has the advantage that your code is not using global variables. It also means that you can apply unit tests to your classes in isolation (you can pass a dummy
Logger into a class when it is being tested, which simply discards messages).
The disadvantage is that almost every class might use logging, so you will need to pass a
Logger object into almost every object you ever create.
The singleton pattern is often criticised as being a global variable in disguise. It indeed shares some of the disadvantages of global variables:
- The state of the singleton is shared globally. Any part of the code can alter the state of the object at any time, which can cause bugs that only happen in very specific circumstances. These types of bugs can be difficult to detect and often difficult to recreate when trying to solve them.
- It can make unit testing difficult.
Care is also required if the system uses concurrent processing (see later).
Still, using a singleton has several advantages compared to a global variable:
- If we had a global
Loggingobject, stored in the variable
LOGGING, it would be possible for buggy code to reassign
LOGGINGto a brand new
Loggingobject, which might cause log messages to be lost. With a singleton, it is impossible for there to ever be a second
- The singleton doesn't directly encourage the use of other globals in the code.
- Since the codebase always creates a
Loggingobject when it needs one, it leaves open the possibility open of switching to an alternative implementation later.
Even so, the singleton pattern is regarded as an anti-pattern by some, and should probably not be used where there are viable alternatives.
We will look at a couple of implementations:
- Making a simple class act as a singleton.
- A singleton base class implementation.
- An alternative using a factory method.
Simple singleton class
Let's start with a simple logger class:
class Logger(): def message(self, str): # Log the message print(str) a = Logger() b = Logger() print(a is b) # False
This simple logger class only has one method,
message, that we can call to write a message to the log. In this implementation, it just prints the message, but in a real implementation, we might write the message to a file.
This class isn't a singleton yet, it is just an ordinary class. Each time we call
Logger() we get a brand new logger object. So in the example,
b are different objects.
That isn't what we want, of course. We would like the first call to
Logger() to create a new
Logger object, but all subsequent calls to return the same object. How do we do that?
We normally use the
__init__ function to initialise an object when it is created. However, there is a different function that is more useful in this case, the
__new__ function. This is the function that actually creates the object, and Python calls it immediately before calling
__init__ whenever it creates a new object. Here is the default implementation of
def __new__(cls, *args, **kwargs): return super(Logger, cls).__new__(cls, *args, **kwargs)
__new__ function doesn't have a
self parameter, because it is called before the new object has even been created. It has a
cls parameter that holds an instance of the class object. It also has
**kwargs parameters that contain any arguments passed into the constructor.
In the default implementation, we simply call the
__new__ method of the superclass
All classes inherit
object. If a class does not specify a base class (which
Logger does not), that means it is directly derived from
If we want to force
Logger to only ever create one unique instance, we need to modify the
__nerw__ method like this:
- The first time
__new__is called, it creates a new object and stores it.
__new__is called again, it just returns the stored object.
Here is the code for the complete singleton:
class Logger(): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Logger, cls).__new__(cls, *args, **kwargs) return cls._instance def message(self, str): # Log the message print(str) a = Logger() b = Logger() print(a is b) # True
Here we have a class variable
_instance, that is initially
__new__ method, when the first
Logger is created, a new object is created and stored in
Logger is called again, the method just returns the previously created object. This means that the constructor will always return the same object. When we call
Logger() twice and save the result in
b, they will both be the same object.
This is a reasonable implementation of a simple singleton.
Initialising the logger
If we were creating a real logger, we would probably need to initialise it with some parameters. For example, if we were logging to file, we might want to pass in a filename. We might normally do this by adding a parameter to the
__init__ method, like this:
def initialize(self, filename): self.filename = filename
The only problem with this is that you need to supply a filename every time you use the constructor.
a = Logger('test.log') b = Logger('test.log')
Since the whole point of the logging singleton is that we should be able to call it easily from anywhere in our code, having to supply a filename each time is a bit of a pain. And there is also the question of what happens (or indeed what should happen) if we accidentally provide a different parameter somewhere in the depths of our code.
It is far easier to use a separate
initialize method to set the parameters. By convention this method is only called once, right at the start of our code:
class Logger(): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Logger, cls).__new__(cls, *args, **kwargs) return cls._instance def initialize(self, filename): self.filename = filename def message(self, str): print(str) Logger().initialize('test.log') a = Logger() b = Logger() print(a is b) print(a.filename)
Here, after initialising the
Logger, we can just call it in the usual way.
Using a singleton base class
A slight problem with the previous class is that the singleton functionality and the logger functionality are both implemented in the same class. It would be nice to separate the singleton implementation into a separate class. This simplifies the logger class, and also makes it easier to re-use the singleton implementation if we ever needed a different singleton.
It is quite easy to separate the functionality:
class Singleton(): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance # Inherits Singleton class Logger(Singleton): def message(self, str): print(str)
Singleton class ensures that only one instance is created. The
Logger class performs the logging and inherits the singleton behaviour from the
We could also create another singleton class, say a database access class, like this:
class Database(Singleton): def query(self, str): # Perform the query print(str)
This class inherits the same singleton behaviour as
Logger because it also derives from the
_instance is a class variable, meaning that there is one variable that is shared between every object in that class. However, even though the class variable is declared in the
Singleton class, that doesn't mean that
Database share the same variable. Every class derived from
Singleton has its own private version of
_instance. For example:
a = Logger() b = Logger() c = Database() d = Database() print(a is b) # True print(c is d) # True print(a is c) # False
This code creates a single
Logger object and a single
Using a factory method
An alternative approach is to use a factory method rather than a singleton. Here is how it works:
class Logger(): def message(self, str): # Log the message print(str) def get_logger(logger=Logger()): return logger a = get_logger() b = get_logger() print(a is b) # True
Logger class is just a simple class that makes no attempt to do anything clever.
Logger class should never be used directly to create a logger object. Instead, the function
get_logger should be used. This function has a
logger parameter with a default value
This works because in Python the default argument is only evaluated once when the function is defined. So each time you call
get_logger you will always receive the same logger object.
This system relies on a convention: you must always use
get_logger, and never call
Logger() directly. If you use this technique, you might consider giving the
Logger class a different name, that discourages direct use, such as
All the techniques above assume a single-threaded environment. If you are using multiple threads in your code, there is a potential problem. It occurs in the
def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Logger, cls).__new__(cls, *args, **kwargs) return cls._instance
Consider the situation where no instance has been created, so
_instance is still set to
None. Now imagine that thread A and thread B both attempt to create an object at the same time.
Let's assume thread A executes
__new__ first. It checks
_instance, finds it is
None, and proceeds to create a new instance.
Now if thread B also executes
__new__, before thread A has finished. Thread B will see that
_instance is still
None and will also decide to create a new instance.
Thread A finishes creating its object and returns it. Soon afterwards, thread B finishes creating its object and returns it. Now the system has two different objects, but all the code has been written assuming there is only one object.
This is potentially a very nasty bug. It might not happen every time, in fact it might even be quite rare. But it could have a devastating effect when it does occur.
There are two ways to avoid this. The first is to make absolutely certain that only one thread is active when the instance is created. This is quite a good solution if the software is initially single-threaded and adds new threads after startup.
The other solution is to place a lock on
__new__ so that only one thread at a time can run it.
We won't look at those solutions in detail here, but it is something to be aware of it using singletons in a multi-threaded environment.
Join the PythonInformer Newsletter
Sign up using this form to receive an email when new content is added:
2d arrays abstract data type alignment and angle animation arc array arrays bar chart bar style behavioural pattern bezier curve built-in function callable object chain circle classes clipping close closure cmyk colour combinations comparison operator comprehension context context manager conversion count creational pattern data science data types decorator design pattern device space dictionary drawing duck typing efficiency ellipse else encryption enumerate fill filter font font style for loop formula function function composition function plot functools game development generativepy tutorial generator geometry gif global variable gradient greyscale higher order function hsl html image image processing imagesurface immutable object in operator index inner function input installing iter iterable iterator itertools join l system lambda function len lerp line line plot line style linear gradient linspace list list comprehension logical operator lru_cache magic method mandelbrot mandelbrot set map marker style matplotlib monad mutability named parameter numeric python numpy object open operator optimisation optional parameter or pandas partial application path pattern permutations pie chart pil pillow polygon pong positional parameter print product programming paradigms programming techniques pure function python standard library radial gradient range recipes rectangle recursion reduce regular polygon repeat rgb rotation roundrect scaling scatter plot scipy sector segment sequence setup shape singleton slice slicing sound spirograph sprite square str stream string stroke structural pattern subpath symmetric encryption template text text metrics tinkerbell fractal transform translation transparency triangle truthy value tuple turtle unpacking user space vectorisation webserver website while loop zip zip_longest