DP 4: Singleton Pattern

A Design (or Anti) Pattern Allowing Only A Single Instance
design patterns
Author

Tony Phung

Published

December 31, 2024

1. Create Singleton Class

Singleton pattern creates a metaclass that limits the number of instance of a particular class to 1

Metaclasses allow the customisation of object instantiation (see metaclasses post)

  • __call__ method is overriden (see: call-magic-method post)

  • self._instances list: keeps track of existence of instance

  • id(cls._instances[cls]): allows us compare object instances

class SingleTonyCls(type):
    _instances = {}
    
    def __call__(cls, *args, **kwds):
        if cls not in cls._instances:
            print(f"{cls} does not exist! Creating new... ")
            object_instance = super().__call__(*args, **kwds)
            cls._instances[cls] = object_instance
            print(f"[{id(object_instance)}] added to {cls} list")
            # print(f"[__name__]: {__name__}") -- main file
            # print(f"[cls]: {cls}") --  class name
            # print(f"[__class__]: {__class__}") metaclass name
        else:
            print(f"{cls} exists: [{id(cls._instances[cls])}] ")
        return cls._instances[cls]

2. Create Classes: Singleton

There should only be a single instances of any class that has metaclass following the singleton pattern.

All objects have the same id.

class FooSGL(metaclass = SingleTonyCls):
    pass

3. Create Classes: Regular Way

There is no limit to unique instances created.

Each object should have a unique id.

class Foo():
    pass

4. Compare Object IDs: Singleton vs Regular Way

4.1 Create Multiple Objects: Singleton

foo_sgl_a = FooSGL()
foo_sgl_b = FooSGL()
foo_sgl_c = FooSGL()
<class '__main__.FooSGL'> does not exist! Creating new... 
[139808669538272] added to <class '__main__.FooSGL'> list
<class '__main__.FooSGL'> exists: [139808669538272] 
<class '__main__.FooSGL'> exists: [139808669538272] 

4.2 Compare Object IDs: Singleton

Each instantiation results in the returning of the first created object evidenced by the same object id

print(id(foo_sgl_a))
print(id(foo_sgl_b))
print(id(foo_sgl_c))
139808669538272
139808669538272
139808669538272

4.3 Create Multiple Objects: Regular Way

foo_a = Foo()
foo_b = Foo()
foo_c = Foo()

4.4 Compare Object IDs: Regular Way

Each instantiation results in a unique object id

print(id(foo_a))
print(id(foo_b))
print(id(foo_c))
139808669355984
139808669358432
139808669357808