Creating a Singleton Class in Python And Why You (Probably) Don’t Need It
Creating a singleton class is a great exercise to understand object creation in Python, but you’re unlikely to need it in your code.
If you spend long enough in the programming world, you’ll come across the term singleton at some point. And if you hadn’t seen this term yet, well, now you have!
In Python, you don’t need singleton classes. Almost never. But creating one is a great exercise in understanding how Python creates objects. And discussing alternatives to a singleton class helps you explore other aspects of Python.
“Hey Stephen, sounds great, but you haven’t told us what this singleton thing is yet!”
Fair point – here we go.
What’s a Singleton?
The singleton pattern is a design pattern in which a class can produce only one instance. Not two, not three, just one. And each time you try to create a new instance of a singleton class, you get the same object again.
Let me pick a trivial example of when you may need this. You’re writing a game. Perhaps several players can play games simultaneously. And you need a leaderboard. But you only want one leaderboard. You don’t want each game to create its own leaderboard. Otherwise, what’s the point of the leaderboard?
There are other examples in programming when singletons appear to be the solution: creating a connection to a database or to a hardware device – you only want one connection – creating a logger or a configuration manager. But they sound too serious and proper. So I’ll stick with the leaderboard in a game example for this discussion.
Creating a Leaderboard • First Attempt
Let’s say you have a Game
class and you create Game
instances each time a game is played. I won’t write this class as it’s not needed, and I want to keep this article relatively brief (famous last words!).
The Game
class needs to access a leaderboard. Each Game
instance needs to access a leaderboard – the same leaderboard. Let’s say you create a class to manage this leaderboard:

You add the .scores
data attribute, which is a dictionary, in the class’s .__init__()
method. If this is all you need, you definitely don’t need a class. But you add some methods to manage the leaderboard:
Now you have a bit more functionality. Let’s review these methods briefly:
.add_score()
adds a score to the leaderboard, as its name implies. If the player already exists in the.scores
dictionary, you add to their points tally. If the player doesn’t exist yet, you add them to the dictionary. There are neater ways to write this method, but this will do here..get_leaderboard()
returns a sorted list containing the players in order, from those with the highest number of points to the lowest. If you’re not familiar withsorted()
, itskey
parameter, andlambda
functions, you can read one of the most popular articles on The Python Coding Stack: The Key To The key Parameter in Python.display()
displays the leaderboard, using.get_leaderboard()
along the way..reset()
resets the leaderboard. By the way, you see why choosing descriptive names matters! I didn’t include the safety verifications and steps you might want to take for this method.
Looks good? Let’s try it out. For simplicity, you can just add these lines to the same script where you define your class:
You create an instance of Leaderboard
and call .add_score()
three times. If we had a Game
class, the first line, which creates a Leaderboard
instance and assigns it to an identifier, would be included in the Game.__init__()
, but here I’m just creating this variable in the main script. Here’s the displayed leaderboard:
Kate: 15
Stephen: 14
All seems fine. Kate is leading with 15 points. I’m second (also last) with 14 points.
But, later in your program, possibly within a different Game
instance, you write the following:
And here’s the output now:
Kate: 15
Stephen: 14
Now Dealing With ‘another_leaderboard’
Sarah: 13
Max: 7
Recall that the first two lines of the output are from the code you wrote earlier.
But where’s Kate in the new leaderboard? And where am I? This code creates a new Leaderboard
object, unrelated to the previous one. You can confirm this by showing the identity of each object, using id()
, or by using the is
operator:
The outputs from these three calls to print()
are below:
4347130752
4347064592
False
The two objects have different identities. They’re not the same object. The identity values you get will be different from mine, but what matters here is that they’re different from each other.
You could make sure you don’t call Leaderboard()
again in your code. But this is not always possible or straightforward. And you’d still need to make sure your documentation makes this really clear. And will your users read the documentation? Who knows. You may be opening the door to bugs.
Creating a Leaderboard • Second Attempt Using a Singleton Class
Now, you may be thinking: “Hey, I read about this great design pattern: the singleton pattern. Surely, this is a great time to use it…”.
The singleton is an important pattern in some languages. (Spoiler alert: it’s not so useful in Python – you’ll see why later). So let’s try to implement it in Python.
Let’s start with this question: “What’s the first thing that happens when you create an instance of a class in Python?”
If you answered: “Python calls its .__init__()
method to initialise the object”, then you’re not quite right. Something else happens first. The .__init__()
method initialises a “blank” instance of the class. But what creates that instance in the first place? It’s not .__init__()
.
It’s .__new__()
. In most cases, you don’t need to worry about defining .__new__()
when you define a class. The default behaviour is good enough. But in this case, you want to have more control over what happens when you create a new instance of the Leaderboard
class:
You add a few things to the class definition:
You add the class attribute
._instance
. This attribute belongs to the class, so each instance won’t have its own version, unlike the data attributes you create in.__init__()
. The leading underscore in its name is a convention indicating that this attribute is meant for internal use only and that users of this class shouldn’t access it directly.You define the special method
.__new__()
. Let’s explore the code in this method further.
When you don’t define a .__new__()
special method, Python uses the default implementation, which is inherited from the base class object
. All Python classes inherit from the object
class. However, since you define a .__new__()
method in your class, Python uses this method instead of the default .__new__()
.
But then you need to call super().__new__()
, which creates the new blank object using object.__new__()
. However, you only do this if the ._instance
class attribute is None
– that’s what if cls._instance is None:
does. Let’s understand what’s happening here.
The first time you create an instance of the class, you create the new blank instance since ._instance
is None
at first. You then assign this new instance to the class attribute ._instance
. The .__new__()
method should return the instance, which is then passed on to .__init__()
. But we’ll get there later.
What happens if you try to create a new Leaderboard
object again? The second time your code calls Leaderboard.__new__()
, the class attribute ._instance
is no longer None
. It now refers to an object of the class. Therefore, the code bypasses the creation of a new object and returns the existing one.
So, is the problem solved? Let’s find out. Here’s a reminder of the code used to explore this class (with a few extra print()
calls):
Here’s the output now:
Show leaderboard:
Kate: 15
Stephen: 14
Show leaderboard:
Sarah: 13
Max: 7
4344263552
4344263552
True
There’s good news and bad news – which one do you want first?
Let’s start with the good news. The variable names leaderboard
and another_leaderboard
refer to the same object. Notice how the identity returned by id()
is the same, and the is
expression returns True
. They’re the same object. When you call Leaderboard()
the second time, your code doesn’t create a new instance. It uses the existing one.
Great.
But the leaderboards displayed are still different. Why?
You now have the same object – you’re not creating a new one. But you’re reinitialising it the second time you call Leaderboard()
. When you call a class to create an instance by using Leaderboard()
(with parentheses), Python first calls the class’s .__new__()
. But you dealt with that already – it doesn’t create a new instance. However, the instance returned by .__new__()
is passed to the class’s .__init__()
.
And here’s a reminder of what your Leaderboard.__init__()
does:
Your instance already exists. It already has a .scores
data attribute with some data in it. However, when you call .__init__()
again, you overwrite .scores
with an empty dictionary. Any data you already had is gone.
So, you now need to fix this, too. What a pain:
If you haven’t seen the built-in hasattr()
function yet, it stands for has attribute. You pass an object and the name of an attribute, and it returns True
or False
depending on whether that object has that attribute!
In this case, you pass self
as the first argument. And self
is the name that refers to the instance you’re dealing with. You also pass the string “initialised”
as the second argument.
If it’s the first time
.__init__()
is called on this object, the object won’t have the attribute.initialised
since it’s created in the.__init__()
method itself. Therefore, the code within theif
block runs, creating.scores
and.initialised
.The second time you try to initialise the same object – and it will be the same object because of the code you wrote in
.__new__()
– the object will already have the.initialised
data attribute. Therefore, the rest of the code in.__init__()
won’t run.
You won’t overwrite .scores
when you call Leaderboard()
a second time, or a third time… You’re ensuring that an object can only be initialised once.
Run the code now:
Show leaderboard:
Kate: 15
Stephen: 14
Show leaderboard:
Kate: 15
Stephen: 14
Sarah: 13
Max: 7
4336350080
4336350080
True
There’s still only one Leaderboard
object. But now, you’re not overwriting any data it already has, either. As you can see, when you call another_leaderboard = Leaderboard()
, you don’t create a new instance. Instead, you reuse the one you already have. And you don’t erase its existing data, but add on to it.
Now, your class can only have one instance of the Leaderboard
class. It’s a singleton!
That’s a lot of work to create a useable singleton class.
And I won’t open the can of worms of the scenario where you may have multiple concurrent threads. Two or more threads may access .__new__()
at the same time. And that’s not good!
The Python Coding Stack is getting bigger and better, and not just because there are more of you reading every week. I’ll send out an email announcing more articles, more goodies and more value soon. Stay tuned.
And make sure you upgrade to a paid subscription to make sure you don’t miss anything–now is a good time to upgrade, before monthly and yearly subscription prices go up (they only go up for new paid subscribers, never for existing ones)
We’ve Learnt Stuff By Creating a Singleton
Creating a singleton class, as you did above, is a useful exercise to help you understand how Python creates and initialises new objects. From a learning perspective, it’s a great exercise.
But, do you need this in your code?
Generally, no.
It’s a lot of work.
There’s an easier way to achieve the same effect in Python (which may not exist in other programming languages).
And maybe you don’t really need a single global object that you refer to from all over your program.
Let’s explore some options. And no, I won’t cover all the options. I said I want to keep this article brief (but I’m already on 2k+ words). My aim here is to start you off on a journey to think about what goes where, when, and how…
Alternative to the Singleton Class • Move The Class to a New Module
Let’s roll back the changes to the Leaderboard
class. Delete the .__new__()
method and the ._instance
class attribute. And revert back to the original, simpler .__init__()
. However, place the class in its own script. Let’s call it leaderboard.py
:
Note how .__new__()
is no longer there and .__init__()
simply creates the .scores
data attribute.
There’s also one more line of code after the class definition – and only one. You create an instance of the class.
Now, let’s go back to your main script, which could be anywhere else in your program. Let’s call the main script main.py
:
The class defined in leaderboard.py
is not a singleton class. It’s a regular class. However, you create the one and only instance you’ll ever create within leaderboard.py
. Then, you simply import this instance using from leaderboard import leaderboard
. The variable name and module name don’t have to be the same, of course, but in this case they are.
Ah, what if you import the module more than once? I can hear you think… Python only loads a module once. Here’s a demonstration. Add the following print()
call to leaderboard.py
:
Now, go back to main.py
and import leaderboard
a second time anywhere else in your code:
Run this code. How many times do you see the text leaderboard.py loaded
in your output?
leaderboard.py loaded
Show leaderboard:
Kate: 15
Stephen: 14
Show leaderboard:
Kate: 15
Stephen: 14
Sarah: 13
Max: 7
Once. Modules are only loaded once. And the final output still shows the correct, combined leaderboard.
And there you go – you can only have one instance of Leaderboard
now, without any of the hassle of messing around with .__new__()
and .__init__()
.
Modules in Python give you a shortcut to create singleton-like behaviour.
Sometimes, You Can Simplify Further
In fact, do you really need the Leaderboard
class? Sometimes, you do, or you prefer to have everything relating to the leaderboard in a single object. In that case, the solutions in the previous section and in the one after this one are great.
But can you live with a simpler option?
Note that some functions’ names are updated to make them more readable since they’re no longer methods in a class.
Sure, this option may not always be possible. And some purists will scoff at these functions accessing and mutating a global variable (although you can modify the functions to accept the dictionary as one of the arguments, if you prefer).
The leading underscore in _scores
indicates that you don’t intend this variable to be used by the user. It’s not meant for public use. Users should only access it through the functions.
If you prefer, you can place ._scores
and the functions in a separate module and import them. As you saw earlier, Python imports a module only once. Therefore, anything defined within a module is effectively a singleton! In Python, the behaviour of modules makes creating a single instance of a class to share across your code much easier – even trivial. Other languages don’t have this option, which is why the singleton pattern exists.
So, if you think a singleton class is the solution to your problem, consider whether this simpler option will do!
Need More Flexibility and Future-Proof Code?
Here’s yet another option. Create a file called game_services.py
:
You can also define the Leaderboard
class within this module, if you prefer, but here I’m leaving it in its own module, leaderboard.py
. The GameServices
class has a single data attribute, .leaderboard
, which contains an instance of Leaderboard
. This instance of Leaderboard
is created when you create an instance of GameServices
, which you do in the final line in this script.
You’re using composition in this case. The GameServices
class has access to Leaderboard
by having a Leaderboard
instance as one of its attributes. You can read more about composition in this pair of articles: Choose Your Fighter • Let’s Play (#1 in Inheritance vs Composition Pair) and Choose Your Fighter • Inheritance vs. Composition (#2 in Inheritance vs Composition Pair).
Back in main.py
, you can now import this game_services
instance:
At first sight, this version seems similar to the first alternative I presented above, just a bit more complex. However, instead of creating an instance of Leaderboard
that is then used elsewhere, in this version, the Leaderboard
instance is included in a container, the GameServices
object. You then use the instance of the GameServices
object wherever needed.
There’s more boilerplate code, but you also get more flexibility with this version. What if you want to replace the leaderboard with a different one for testing purposes? The classic singleton class is hard to test. This option simplifies things because you can assign a new Leaderboard
to game_services.leaderboard
or create a separate GameServices
object for this purpose. Nothing else needs to change in your code.
You can also use a different implementation of Leaderboard
, say you have an AlternativeLeaderboard
class you want to experiment with. It’s easier and safer to make these changes when the leaderboard is included in the GameServices
object.
And what if you later decide you want multiple leaderboards? Perhaps one for a version of the game and a different leaderboard for another version of the game? You no longer want a singleton! But with this version of the code, you can easily create another data attribute in GameServices
. Sure, you’d be able to do so if using Leaderboard
directly, as in the first example. But this option makes it safer and easier to expand your code.
And perhaps, you have other services you want to share, not just a leaderboard. You can also add more data attributes.
Final Words
Note how the alternatives of the singleton class use standard classes that don’t need a .__new__()
and extra work in the .__init__()
, or they don’t use classes at all. They rely on composition within another class and on the fact that Python loads a module only once per program, so instances created in a module are effectively singletons when used elsewhere in the program.
There are other alternatives for situations where you may be tempted to use a singleton. And there may be some less common scenarios when the answer is still to create a singleton class.
So, I’m not stating that you absolutely never need to create a singleton class in Python. But in most cases, there are neater and more Pythonic alternatives.
Still, creating a singleton class, as we did earlier in this article, is a useful learning exercise!
This publication is entirely supported by its readers – there are no adverts, no sponsors! But it takes a lot of effort and time to get one of these articles out. If you want to support this publication further, and get exclusive articles, videos, and more goodies, you can become a paid subscriber.
You can also support this publication by making a one-off contribution of any amount you wish.
Photo by Marek Piwnicki: https://www.pexels.com/photo/born-by-the-stars-17806401/
Code in this article uses Python 3.14
The code images used in this article are created using Snappify. [Affiliate link]
You can also support this publication by making a one-off contribution of any amount you wish.
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com
Further reading related to this article’s topic:
Choose Your Fighter • Let’s Play (#1 in Inheritance vs Composition Pair) and Choose Your Fighter • Inheritance vs. Composition (#2 in Inheritance vs Composition Pair)
Appendix: Code Blocks
Code Block #1
class Leaderboard:
def __init__(self):
self.scores = {}
Code Block #2
class Leaderboard:
def __init__(self):
self.scores = {}
def add_score(self, player, score):
if player in self.scores:
self.scores[player] += score
else:
self.scores[player] = score
def get_leaderboard(self):
return sorted(
self.scores.items(),
key=lambda item: item[1],
reverse=True,
)
def display(self):
for player, score in self.get_leaderboard():
print(f”{player}: {score}”)
def reset(self):
# You may want to add a confirmation step in a real application,
# or save a backup to a file first
self.scores.clear()
Code Block #3
# ...
leaderboard = Leaderboard()
leaderboard.add_score(”Stephen”, 10)
leaderboard.add_score(”Kate”, 15)
leaderboard.add_score(”Stephen”, 4)
leaderboard.display()
Code Block #4
# ...
# later in the game, or in another game instance...
print(”\nNow Dealing With ‘another_leaderboard’”)
another_leaderboard = Leaderboard()
another_leaderboard.add_score(”Max”, 7)
another_leaderboard.add_score(”Sarah”, 13)
another_leaderboard.display()
Code Block #5
# ...
print(id(leaderboard))
print(id(another_leaderboard))
print(leaderboard is another_leaderboard)
Code Block #6
class Leaderboard:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# ...
Code Block #7
# ...
leaderboard = Leaderboard()
leaderboard.add_score(”Stephen”, 10)
leaderboard.add_score(”Kate”, 15)
leaderboard.add_score(”Stephen”, 4)
print(”Show leaderboard:”)
leaderboard.display()
# later in the game, or in another game instance...
another_leaderboard = Leaderboard()
another_leaderboard.add_score(”Max”, 7)
another_leaderboard.add_score(”Sarah”, 13)
print(”\nShow leaderboard:”)
another_leaderboard.display()
print()
print(id(leaderboard))
print(id(another_leaderboard))
print(leaderboard is another_leaderboard)
Code Block #8
class Leaderboard:
# ...
def __init__(self):
self.scores = {}
# ...
Code Block #9
class Leaderboard:
# ...
def __init__(self):
# Prevent reinitialisation
if not hasattr(self, “initialised”):
self.scores = {}
self.initialised = True
# ...
Code Block #10
# leaderboard.py
class Leaderboard:
def __init__(self):
self.scores = {}
def add_score(self, player, score):
if player in self.scores:
self.scores[player] += score
else:
self.scores[player] = score
def get_leaderboard(self):
return sorted(
self.scores.items(),
key=lambda item: item[1],
reverse=True,
)
def display(self):
for player, score in self.get_leaderboard():
print(f”{player}: {score}”)
def reset(self):
# You may want to add a confirmation step in a real application,
# or save a backup to a file first
self.scores.clear()
# Create a single instance of Leaderboard
leaderboard = Leaderboard()
Code Block #11
# main.py
from leaderboard import leaderboard
leaderboard.add_score(”Stephen”, 10)
leaderboard.add_score(”Kate”, 15)
leaderboard.add_score(”Stephen”, 4)
print(”Show leaderboard:”)
leaderboard.display()
# later in the game, or in another game instance...
leaderboard.add_score(”Max”, 7)
leaderboard.add_score(”Sarah”, 13)
print(”\nShow leaderboard:”)
leaderboard.display()
Code Block #12
# leaderboard.py
print(”leaderboard.py loaded”)
class Leaderboard:
# ...
Code Block #13
# main.py
from leaderboard import leaderboard
leaderboard.add_score(”Stephen”, 10)
leaderboard.add_score(”Kate”, 15)
leaderboard.add_score(”Stephen”, 4)
print(”Show leaderboard:”)
leaderboard.display()
# Note, we usually never place imports in the middle of a file,
# but this is just to illustrate that the singleton instance
# is shared even if we import it again.
from leaderboard import leaderboard
# later in the game, or in another game instance...
leaderboard.add_score(”Max”, 7)
leaderboard.add_score(”Sarah”, 13)
print(”\nShow leaderboard:”)
leaderboard.display()
Code Block #14
_scores = {}
def add_score(player, score):
if player in _scores:
_scores[player] += score
else:
_scores[player] = score
def get_leaderboard():
return sorted(
_scores.items(),
key=lambda item: item[1],
reverse=True,
)
def display_leaderboard():
for player, score in get_leaderboard():
print(f”{player}: {score}”)
def reset_leaderboard():
# You may want to add a confirmation step in a real application,
# or save a backup to a file first
_scores.clear()
add_score(”Stephen”, 10)
add_score(”Kate”, 15)
add_score(”Stephen”, 4)
print(”Show leaderboard:”)
display_leaderboard()
# later in the game, or in another game instance...
add_score(”Max”, 7)
add_score(”Sarah”, 13)
print(”\nShow leaderboard:”)
display_leaderboard()
Code Block #15
# game_services.py
from leaderboard import Leaderboard
class GameServices:
def __init__(self):
self.leaderboard = Leaderboard()
game_services = GameServices()
Code Block #16
# main.py
from game_services import game_services
game_services.leaderboard.add_score(”Stephen”, 10)
game_services.leaderboard.add_score(”Kate”, 15)
game_services.leaderboard.add_score(”Stephen”, 4)
print(”Show leaderboard:”)
game_services.leaderboard.display()
# later in the game, or in another game instance...
game_services.leaderboard.add_score(”Max”, 7)
game_services.leaderboard.add_score(”Sarah”, 13)
print(”\nShow leaderboard:”)
game_services.leaderboard.display()
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com