Exist Technote

[Python] Singleton Pattern (싱글톤 패턴) 본문

Design Pattern/Creational

[Python] Singleton Pattern (싱글톤 패턴)

by_Exist 2020. 9. 26. 18:32

https://refactoring.guru/design-patterns/singleton

Singleton이란?

  • 하나의 클래스가 여러 차례 호출되더라도 최초 호출시에 생성된 객체만을 반환하는 클래스.
  • 공통된 객체를 여러개 생성해서 사용하는 DBCP(DataBase Connection Pool)와 같은 상황에서 사용된다.

시작하기 전에

  • 싱글톤이 안티패턴이냐 아니냐, 특정 싱글톤이 멀티쓰레드 환경에서 안전하냐 안전하지 않느냐 등의 토론을 한번 검색하여 찾아보길 추천한다. (...)
  • 파이썬의 모듈을 싱글톤으로 활용할 수 있다고 한다.
  • "클래스가 여러 차례 호출되더라도 최초 호출시에 생성된 객체만을 반환"에 집중하여 여러 예제들을 나열한다.

예제

생성자를 활용하여 구현된 싱글톤

class Singleton(object):
    # Singleton 클래스를 상속한 클래스가 자신의 단일 인스턴스를 보관한다.
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

class MyClass(Singleton, BaseClass):
    pass

메타클래스로 구현한 싱글톤

class Singleton(type):
    # Singleton 메타클래스가 'Singleton 클래스의 인스턴스(클래스)의 단일 인스턴스'들을 보관한다.
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class MyClass(BaseClass, metaclass=Singleton):
    pass

클래스메소드를 활용하여 구현한 싱글톤

class Singleton(object):

    __instance = None

    @classmethod
    def __getInstance(cls):
        return cls.__instance

    @classmethod
    def instance(cls, *args, **kargs):
        cls.__instance = cls(*args, **kargs)    # 첫 호출시, instance 함수로 객체를 생성하고
        cls.instance = cls.__getInstance    # __getInstance 함수를 instance 변수에 덮어씌워버리는 방식.
        return cls.__instance

class MyClass(BaseClass, Singleton):
    pass

c = MyClass.instance()

데코레이터를 활용하여 구현한 싱글톤

import functools

def singleton(cls):

    previous_instances = {}
    # 'singleton 함수의 previous_instances' 내에 'singleton 함수로 데코레이트한 싱글톤 객체들'을 보관한다.

    @functools.wraps(cls)
    def wrapper(*args, **kwargs):
        # 타 싱글톤과 달리, 생성자가 다를 경우 각각의 싱글톤을 지닌다. (이런 방법이...!)
        if cls in previous_instances and previous_instances.get(cls, None).get('args') == (args, kwargs):
            return previous_instances[cls].get('instance')
        else:
            previous_instances[cls] = {
                'args': (args, kwargs),
                'instance': cls(*args, **kwargs)
            }
            return previous_instances[cls].get('instance')

    return wrapper

@singleton
class BaseClass:
    pass

c = BaseClass()

참고 사이트

handy-decorators
파이썬 조각 코드 모음집

Comments