Skip to content

Python:Singleton

Python 에서 구현한 Singleton 패턴.

Method type

여기 예제중에서 call 속도도 가장 빠르고 여러가지 상속이나 기타 상황에서 가장 문제가 없는 방식이다. 사용 방식은 C/C++에서 자주사용하는 방식과 비슷하게 직접 instance 메소드를 실행하는 방법이다. 상속 순서역시 가장 뒤에 와도 상관없기 때문에 가장 좋은 방법인것 같다.

class SingletonInstane:
  __instance = None

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

  @classmethod
  def instance(cls, *args, **kargs):
    cls.__instance = cls(*args, **kargs)
    cls.instance = cls.__getInstance
    return cls.__instance

class MyClass(BaseClass, SingletonInstane):
  pass

c = MyClass.instance()

A decorator

빠르지만 클래스 자체를 호출할경우 함수가 리턴되기 때문에 클래스의 StaticMethod에 접근할수가 없다..

def singleton(class_):
  instances = {}
  def getinstance(*args, **kwargs):
    if class_ not in instances:
        instances[class_] = class_(*args, **kwargs)
    return instances[class_]
  return getinstance

@singleton
class MyClass(BaseClass):
  pass

A base class

데코레이터 타입의 싱글톤보다 아주 살짝 느리지만 일반적인 클래스처럼 쓸 수가 있기 때문에 가장 범용적이다.

class Singleton(object):
  _instance = None
  def __new__(class_, *args, **kwargs):
    if not isinstance(class_._instance, class_):
        class_._instance = object.__new__(class_, *args, **kwargs)
    return class_._instance

class MyClass(Singleton, BaseClass):
  pass

A metaclass

class Singleton(type):
    _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]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

Decorator returning a class with the same name

데코레이터로 사용하며, 클래스명(__name__ 속성)을 덮어쓰고, object.__new__(cls) 오류를 해결한 버전:

# -*- coding: utf-8 -*-

from inspect import signature


def singleton(base):
    class _Wrapper(base):
        __singleton_instance__ = None

        def __new__(cls, *args, **kwargs):
            if _Wrapper.__singleton_instance__ is None:
                super_cls = super(_Wrapper, cls)
                if object.__new__ == super_cls.__new__:
                    instance = super_cls.__new__(cls)
                else:
                    new_sig = signature(super_cls.__new__)
                    ba = new_sig.bind(cls, *args, **kwargs)
                    ba.apply_defaults()
                    instance = super_cls.__new__(*ba.args, **ba.kwargs)
                _Wrapper.__singleton_instance__ = instance
                _Wrapper.__singleton_instance__.__singleton_sealed__ = False
            return _Wrapper.__singleton_instance__

        def __init__(self, *args, **kwargs):
            if self.__singleton_sealed__:
                return
            super(_Wrapper, self).__init__(*args, **kwargs)
            self.__singleton_sealed__ = True

        @staticmethod
        def get_singleton_instance() -> base:
            return _Wrapper.__singleton_instance__

    _Wrapper.__name__ = base.__name__
    return _Wrapper

유닛 테스트:

# -*- coding: utf-8 -*-

from unittest import TestCase, main

from cw.patterns.singleton import singleton


@singleton
class _TestNoInheritance:
    def __init__(self, value=999):
        self.value = value

    @classmethod
    def name(cls):
        return cls.__name__


class _TestBase:
    def __new__(cls, value=888):
        return object.__new__(cls)

    def __init__(self, value=999):
        self.value = value

    @classmethod
    def name(cls):
        return cls.__name__


@singleton
class _TestInheritance(_TestBase):
    def __init__(self, value=999):
        super().__init__(value)

    @classmethod
    def name(cls):
        return cls.__name__


class SingletonTestCase(TestCase):
    def test_no_inheritance(self):
        o0 = _TestNoInheritance(100)
        o1 = _TestNoInheritance(200)
        o2 = _TestNoInheritance(300)
        o3 = _TestNoInheritance()

        self.assertTrue(hasattr(o0, "__singleton_instance__"))
        self.assertTrue(hasattr(o1, "__singleton_instance__"))
        self.assertTrue(hasattr(o2, "__singleton_instance__"))
        self.assertTrue(hasattr(o3, "__singleton_instance__"))

        self.assertIsNotNone(getattr(o0, "__singleton_instance__"))
        self.assertIsNotNone(getattr(o1, "__singleton_instance__"))
        self.assertIsNotNone(getattr(o2, "__singleton_instance__"))
        self.assertIsNotNone(getattr(o3, "__singleton_instance__"))

        self.assertTrue(hasattr(o0, "__singleton_sealed__"))
        self.assertTrue(hasattr(o1, "__singleton_sealed__"))
        self.assertTrue(hasattr(o2, "__singleton_sealed__"))
        self.assertTrue(hasattr(o3, "__singleton_sealed__"))

        self.assertEqual(o0.value, 100)
        self.assertEqual(o1.value, 100)
        self.assertEqual(o2.value, 100)
        self.assertEqual(o3.value, 100)

        self.assertEqual(o0.name(), "_TestNoInheritance")
        self.assertEqual(o1.name(), "_TestNoInheritance")
        self.assertEqual(o2.name(), "_TestNoInheritance")
        self.assertEqual(o3.name(), "_TestNoInheritance")

        self.assertEqual(id(o0), id(o1))
        self.assertEqual(id(o0), id(o2))
        self.assertEqual(id(o0), id(o3))

    def test_inheritance(self):
        o0 = _TestInheritance(100)
        o1 = _TestInheritance(200)
        o2 = _TestInheritance(300)
        o3 = _TestInheritance()

        self.assertTrue(hasattr(o0, "__singleton_instance__"))
        self.assertTrue(hasattr(o1, "__singleton_instance__"))
        self.assertTrue(hasattr(o2, "__singleton_instance__"))
        self.assertTrue(hasattr(o3, "__singleton_instance__"))

        self.assertIsNotNone(getattr(o0, "__singleton_instance__"))
        self.assertIsNotNone(getattr(o1, "__singleton_instance__"))
        self.assertIsNotNone(getattr(o2, "__singleton_instance__"))
        self.assertIsNotNone(getattr(o3, "__singleton_instance__"))

        self.assertTrue(hasattr(o0, "__singleton_sealed__"))
        self.assertTrue(hasattr(o1, "__singleton_sealed__"))
        self.assertTrue(hasattr(o2, "__singleton_sealed__"))
        self.assertTrue(hasattr(o3, "__singleton_sealed__"))

        self.assertEqual(o0.value, 100)
        self.assertEqual(o1.value, 100)
        self.assertEqual(o2.value, 100)
        self.assertEqual(o3.value, 100)

        self.assertEqual(o0.name(), "_TestInheritance")
        self.assertEqual(o1.name(), "_TestInheritance")
        self.assertEqual(o2.name(), "_TestInheritance")
        self.assertEqual(o3.name(), "_TestInheritance")

        self.assertEqual(id(o0), id(o1))
        self.assertEqual(id(o0), id(o2))
        self.assertEqual(id(o0), id(o3))


if __name__ == "__main__":
    main()

Favorite site