Skip to content

Singleton Design Pattern

Video Lecture

Section Video Links
Singleton Overview Singleton Overview Singleton Overview Singleton Overview 
Singleton Use Case Singleton Use Case Singleton Use Case Singleton Use Case 
Python Dictionary Python Dictionary Python Dictionary Python Dictionary 

Overview

Sometimes you need an object in an application where there is only one instance.

You don't want there to be many versions, for example, you have a game with a score and you want to adjust it. You may have accidentally created several instances of the class holding the score object. Or, you may be opening a database connection, there is no need to create many, when you can use the existing one that is already in memory. You may want a logging component, and you want to ensure all classes use the same instance. So, every class could declare their own logger component, but behind the scenes, they all point to the same memory address (id).

By creating a class using the Singleton pattern, you can enforce that even if a second instance was created, it will still refer to the original.

The Singleton can be accessible globally, but it is not a global variable. It is a class that can be instanced at any time, but after it is first instanced, any new instances will point to the same instance as the first.

For a class to behave as a Singleton, it should not contain any references to self but use static variables, static methods and/or class methods.

Singleton UML Diagram

Singleton UML Diagram

Source Code

...Refer to Book or Videos for extra content.

./singleton/singleton_concept.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# pylint: disable=too-few-public-methods
"Singleton Concept Sample Code"
import copy

class Singleton():
    "The Singleton Class"
    value = []

    def __new__(cls):
        return cls

    # def __init__(self):
    #     print("in init")

    @staticmethod
    def static_method():
        "Use @staticmethod if no inner variables required"

    @classmethod
    def class_method(cls):
        "Use @classmethod to access class level variables"
        print(cls.value)

# The Client
# All uses of singleton point to the same memory address (id)
print(f"id(Singleton)\t= {id(Singleton)}")

OBJECT1 = Singleton()
print(f"id(OBJECT1)\t= {id(OBJECT1)}")

OBJECT2 = copy.deepcopy(OBJECT1)
print(f"id(OBJECT2)\t= {id(OBJECT2)}")

OBJECT3 = Singleton()
print(f"id(OBJECT1)\t= {id(OBJECT3)}")

Output

1
2
3
4
5
python ./singleton/singleton_concept.py
id(Singleton)   = 2164775087968
id(OBJECT1)     = 2164775087968
id(OBJECT2)     = 2164775087968
id(OBJECT3)     = 2164775087968

Notes

Variables declared at class level are static variables and can be accessed directly using the class name without the class needing to be instantiated first.

cls is a reference to the class

self is a reference to the instance of the class

_new_ gets called before _init_,

_new_ has access to class level variables

_init_ references self that is created when the class is instantiated

By using _new_, and returning a reference to cls, we can force the class to act as a singleton. For a class to act as a singleton, it should not contain any references to self.

Example Use Case

...Refer to Book or Videos for extra content.

Example UML Diagram

Singleton Use Case Diagram

Source Code

./singleton/client.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# pylint: disable=too-few-public-methods

"Singleton Use Case Example Code."

from game1 import Game1
from game2 import Game2
from game3 import Game3

# The Client
# All games share and manage the same leaderboard because it is a singleton.
GAME1 = Game1()
GAME1.add_winner(2, "Cosmo")

GAME2 = Game2()
GAME2.add_winner(3, "Sean")

GAME3 = Game3()
GAME3.add_winner(1, "Emmy")

GAME1.leaderboard.print()
GAME2.leaderboard.print()
GAME3.leaderboard.print()

./singleton/game1.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
"A Game Class that uses the Leaderboard Singleton"

from leaderboard import Leaderboard
from interface_game import IGame

class Game1(IGame):  # pylint: disable=too-few-public-methods
    "Game1 implements IGame"

    def __init__(self):
        self.leaderboard = Leaderboard()

    def add_winner(self, position, name):
        self.leaderboard.add_winner(position, name)

./singleton/game2.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
"A Game Class that uses the Leaderboard Singleton"

from leaderboard import Leaderboard
from interface_game import IGame

class Game2(IGame):  # pylint: disable=too-few-public-methods
    "Game2 implements IGame"

    def __init__(self):
        self.leaderboard = Leaderboard()

    def add_winner(self, position, name):
        self.leaderboard.add_winner(position, name)

./singleton/game3.py

1
2
3
4
5
6
"A Game Class that uses the Leaderboard Singleton"

from game2 import Game2

class Game3(Game2):  # pylint: disable=too-few-public-methods
    """Game 3 Inherits from Game 2 instead of implementing IGame"""

./singleton/leaderboard.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
"A Leaderboard Singleton Class"

class Leaderboard():
    "The Leaderboard as a Singleton"
    _table = {}

    def __new__(cls):
        return cls

    @classmethod
    def print(cls):
        "A class level method"
        print("-----------Leaderboard-----------")
        for key, value in sorted(cls._table.items()):
            print(f"|\t{key}\t|\t{value}\t|")
        print()

    @classmethod
    def add_winner(cls, position, name):
        "A class level method"
        cls._table[position] = name

./singleton/interface_game.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# pylint: disable=too-few-public-methods
"A Game Interface"

from abc import ABCMeta, abstractmethod

class IGame(metaclass=ABCMeta):
    "A Game Interface"
    @staticmethod
    @abstractmethod
    def add_winner(position, name):
        "Must implement add_winner"

Output

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
python ./singleton/client.py
-----------Leaderboard-----------
|       1       |       Emmy    |
|       2       |       Cosmo   |
|       3       |       Sean    |

-----------Leaderboard-----------
|       1       |       Emmy    |
|       2       |       Cosmo   |
|       3       |       Sean    |

-----------Leaderboard-----------
|       1       |       Emmy    |
|       2       |       Cosmo   |
|       3       |       Sean    |

New Coding Concepts

Python Dictionary

In the file ./singleton/leaderboard.py,

4
5
    "The Leaderboard as a Singleton"
    _table = {}

The {} is indicating a Python Dictionary.

A Dictionary can be instantiated using the curly braces {} or dict()

The Dictionary is similar to a List, except that the items are key:value pairs.

The Dictionary can store multiple key:value pairs, they can be changed, can be added and removed, can be re-ordered, can be pre-filled with key:value pairs when instantiated and is very flexible.

Since Python 3.7, dictionaries are ordered in the same way that they are created.

The keys of the dictionary are unique.

You can refer to the dictionary items by key, which will return the value.

1
2
3
4
PS> python
>>> items = {"abc": 123, "def": 456, "ghi": 789}
>>> items["abc"] 
123

You can change the value at a key,

1
2
3
4
5
PS> python
>>> items = {"abc": 123, "def": 456, "ghi": 789}
>>> items["def"] = 101112 
>>> items["def"]
101112

You can add new key:value pairs, and remove them by using the key.

1
2
3
4
5
6
7
8
9
PS> python
>>> items = {"abc": 123, "def": 456, "ghi": 789}
>>> items["jkl"] = 101112
>>> items["jkl"]
101112
>>> items.pop('def')
456
>>> items
{'abc': 123, 'ghi': 789, 'jkl': 101112}

You can order a dictionary alphabetically by key

1
2
3
4
5
6
PS> python
>>> items = {"abc": 123, "ghi": 789, "def": 456}
>>> items
{'abc': 123, 'ghi': 789, 'def': 456}
>>> dict(sorted(items.items()))
{'abc': 123, 'def': 456, 'ghi': 789}

Summary

...Refer to Book or Videos for extra content.