본문 바로가기
Python

[Python] Singleton Pattern

by llHoYall 2021. 11. 2.

Singleton is a creational design pattern, which ensures that only one object of its kind exists and provides a single point of access to it for any other code.

Singleton is alike to the global variable.

Code

class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]

class Singleton(metaclass=SingletonMeta):
    pass

There are several ways to implement the singleton pattern.

Therefore, of course, You can use different methods.

Test

s1 = Singleton()
s2 = Singleton()

if s1 is s2:
    print("Same")
else:
    print("different")
# Same

It works well.

Usage

Now, let's use this singleton pattern with business logic.

class Singleton(metaclass=SingletonMeta):
    def __init__(self, num: int) -> None:
        self.num = num

    def increase(self) -> None:
        self.num += 1

s1 = Singleton(0)
s2 = Singleton(0)

s1.increase()
s2.increase()

print(s1.num)   # 2
print(s2.num)   # 2

The instance with the initial value is returned when firstly called, and the same instance is returned when called again.

Play with this example!

Thread-Safe Code

Let's change the singleton to thread-safe.

from threading import Lock

class SingletonMeta(type):
    _instances = {}

    _lock: Lock = Lock()

    def __call__(cls, *args, **kwargs):
        with cls._lock:
            if cls not in cls._instances:
                instance = super().__call__(*args, **kwargs)
                cls._instances[cls] = instance
        return cls._instances[cls]

class Singleton(metaclass=SingletonMeta):
    pass

Let's use it with threads.

from threading import Lock, Thread

class Singleton(metaclass=SingletonMeta):
    def __init__(self, num: int) -> None:
        self.num = num

    def increase(self) -> None:
        self.num += 1

def test_singleton(num: int) -> None:
    singleton = Singleton(num)
    print("before: ",singleton.num)
    singleton.increase()
    print("after: ",singleton.num)

process1 = Thread(target=test_singleton, args=(2, ))
process2 = Thread(target=test_singleton, args=(7, ))
process1.start()
process2.start()
# before:  2
# after:  3
# before:  3
# after:  4

Great!

Feel free to use this awesome stuff.

댓글