[python] 파이썬에서 싱글 톤 만들기

이 질문은 싱글 톤 디자인 패턴 이 바람직한 지 아닌지 , 반 패턴 또는 종교적 전쟁에 대한 토론이 아니라 파이썬 에서이 패턴이 가장 파이썬적인 방식으로 가장 잘 구현되는 방법에 대해 논의하는 것입니다. 이 예에서 나는 ‘가장 pythonic’을 ‘최소한 놀랍게도의 원리’를 따른다는 것을 의미합니다 .

싱글 톤이 될 여러 클래스가 있습니다 (사용 사례는 로거를위한 것이지만 이것은 중요하지 않습니다). 나는 단순히 상속하거나 장식 할 수있을 때 잇몸이 추가 된 여러 클래스를 어지럽히고 싶지 않습니다.

최선의 방법 :


방법 1 : 데코레이터

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

찬성

  • 데코레이터는 종종 다중 상속보다 직관적 인 방식으로 추가됩니다.

단점

  • MyClass ()를 사용하여 생성 된 객체는 진정한 싱글 톤 객체이지만 MyClass 자체는 클래스가 아니라 함수이므로 클래스 메서드를 호출 할 수 없습니다. 또한 m = MyClass(); n = MyClass(); o = type(n)();그때m == n && m != o && n != o

방법 2 : 기본 클래스

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

찬성

  • 진정한 클래스입니다

단점

  • 다중 상속-eugh! __new__두 번째 기본 클래스에서 상속하는 동안 덮어 쓸 수 있습니까? 필요 이상으로 생각해야합니다.

방법 3 : 메타 클래스

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

찬성

  • 진정한 클래스입니다
  • 상속을 자동으로 다루기
  • 용도 __metaclass__적절한 목적을 위해 (그리고 그것의 나를 인식하게)

단점

  • 거기 아무도 없나요?

방법 4 : 이름이 같은 클래스를 반환하는 데코레이터

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

찬성

  • 진정한 클래스입니다
  • 상속을 자동으로 다루기

단점

  • 각각의 새 클래스를 만드는 데 오버 헤드가 없습니까? 여기서 우리는 싱글 톤으로 만들 각 클래스에 대해 두 개의 클래스를 만듭니다. 내 경우에는 괜찮지 만 이것이 확장되지 않을까 걱정됩니다. 물론이 패턴을 확장하기가 너무 쉬운 지에 대해서는 논쟁의 여지가 있습니다 …
  • _sealed속성 의 요점은 무엇입니까
  • 기본 클래스에서 동일한 이름의 메소드를 호출 할 수 없습니다 super(). 즉 __new__, 호출해야하는 클래스를 사용자 정의 할 수없고 서브 클래스 할 수 없습니다 __init__.

방법 5 : 모듈

모듈 파일 singleton.py

찬성

  • 단순한 것이 복잡한 것보다 낫다

단점



답변

메타 클래스 사용

방법 # 2를 권장 하지만 기본 클래스보다 메타 클래스를 사용하는 것이 좋습니다 . 다음은 샘플 구현입니다.

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]

class Logger(object):
    __metaclass__ = Singleton

또는 Python3에서

class Logger(metaclass=Singleton):
    pass

__init__클래스가 호출 될 때마다 실행하려면 추가하십시오.

        else:
            cls._instances[cls].__init__(*args, **kwargs)

if진술에 Singleton.__call__.

메타 클래스에 대한 몇 마디. 메타 클래스는 클래스의 클래스입니다 . 즉, 클래스는 메타 클래스인스턴스입니다 . 를 사용하여 Python에서 객체의 메타 클래스를 찾을 수 type(obj)있습니다. 일반적인 새 스타일 클래스는 유형 type입니다. Logger위의 코드에서의 class 'your_module.Singleton'(전용) 인스턴스가 유형 과 마찬가지로 Logger유형이 class 'your_module.Logger'됩니다. 당신이 로거를 호출 할 때 Logger(), 파이썬은 처음의 메타 클래스 요청 Logger, Singleton, 인스턴스 생성을 선점 할 수 있도록 무엇을해야합니다. 이 프로세스는 파이썬이 클래스 __getattr__의 속성 중 하나를 참조 하여 호출 할 때 호출하여 수행 할 작업을 묻는 것과 같습니다 myclass.attribute.

메타 클래스는 본질적으로 클래스 의 정의무엇을 의미 하고 그 정의를 구현하는 방법을 결정합니다. 예를 들어 http://code.activestate.com/recipes/498149/를 참조하십시오 struct. 메타 클래스를 사용하여 Python에서 C 스타일을 본질적으로 재생성 합니다. 스레드 메타 클래스에 대한 (콘크리트) 사용 사례는 무엇입니까? 또한 일부 예제를 제공하는데, 일반적으로 특히 ORM에서 사용되는 선언적 프로그래밍과 관련이있는 것 같습니다.

이 상황에서 메소드 # 2 를 사용 하고 서브 클래스가 __new__메소드를 정의하는 경우, 메소드는 저장된 인스턴스를 리턴하는 메소드를 호출해야하기 때문에 호출 할 때마다 실행됩니다SubClassOfSingleton() . 메타 클래스를 사용 하면 유일한 인스턴스가 생성 될 때 한 번만 호출 됩니다. 클래스 호출의 의미사용자 정의하려고합니다 . 유형에 따라 결정됩니다.

일반적으로, 의미가 싱글 톤을 구현하는 메타 클래스를 사용합니다. 싱글 톤은 한 번만 생성 되기 때문에 특별 하며 메타 클래스는 클래스 생성을 사용자 정의하는 방법 입니다. 메타 클래스를 사용하면 싱글 톤 클래스 정의를 다른 방법으로 사용자 정의해야 할 경우에 대비 하여 더 많은 제어 가 가능합니다.

단일 클래스에는 다중 상속이 필요하지 않지만 (메타 클래스는 기본 클래스가 아니기 때문에) 다중 상속을 사용 하는 생성 된 클래스의 하위 클래스의 경우 단일 클래스가 재정의하는 메타 클래스가있는 첫 번째 / 가장 왼쪽 클래스인지 확인해야합니다. __call__이것은 문제가되지 않을 것입니다. 인스턴스 dict가 인스턴스의 네임 스페이스에 없으므로 실수로 덮어 쓰지 않습니다.

또한 싱글 톤 패턴이 “Single Responsibility Principle”을 위반한다는 말을들을 입니다. 각 수업은 한 가지만해야 합니다. 그렇게하면 코드가 분리되어 캡슐화되어 있기 때문에 코드를 변경해야 할 때 코드가 수행하는 작업을 엉망으로 만들 염려가 없습니다. 메타 클래스 구현 은이 테스트를 통과합니다 . 메타 클래스는 패턴시행하는 역할을 하며 생성 된 클래스와 서브 클래스 는 싱글 톤임을 인식 할 필요가 없다 . “MyClass 자체는 클래스가 아니라 함수이므로 클래스 메서드를 호출 할 수 없습니다.”로 언급했듯이 메서드 # 1 은이 테스트에 실패합니다.

파이썬 2와 3 호환 버전

Python2와 3에서 작동하는 것을 작성하려면 약간 더 복잡한 체계를 사용해야합니다. 메타 클래스는 일반적으로 유형의 서브 클래스이므로 메타 클래스 type를 사용하여 런타임에 중개 기본 클래스를 동적으로 작성하고이를 공개 기본 클래스의 기본 클래스 로 사용 하는 것이 가능 합니다 Singleton. 다음 그림과 같이 설명하는 것보다 설명하기가 어렵습니다.

# works in Python 2 & 3
class _Singleton(type):
    """ A metaclass that creates a Singleton base class when called. """
    _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 Singleton(_Singleton('SingletonMeta', (object,), {})): pass

class Logger(Singleton):
    pass

이 접근법의 아이러니 한 측면은 메타 클래스를 구현하기 위해 서브 클래 싱을 사용한다는 것입니다. 한 가지 가능한 장점은 순수 메타 클래스와 달리을 isinstance(inst, Singleton)반환한다는 것 True입니다.

수정

다른 주제에서 이미 눈치 채 셨을 수도 있지만 원래 게시물의 기본 클래스 구현이 잘못되었습니다. _instances요구가되어서는 클래스에서 참조 , 당신은 사용할 필요 super()나있는 거 재귀 , 그리고 __new__당신이해야한다는 사실은 정적의 방법 으로 클래스 패스 실제 클래스로,하지 클래스 메소드를 생성되지 않은 아직 때를 호출됩니다. 메타 클래스 구현에서도 이러한 모든 사항이 적용됩니다.

class Singleton(object):
  _instances = {}
  def __new__(class_, *args, **kwargs):
    if class_ not in class_._instances:
        class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
    return class_._instances[class_]

class MyClass(Singleton):
  pass

c = MyClass()

클래스를 돌려주는 데코레이터

원래 의견을 쓰고 있었지만 너무 길어서 여기에 추가하겠습니다. 방법 # 4 는 다른 데코레이터 버전보다 낫지 만 싱글 톤에 필요한 것보다 더 많은 코드이며, 그것이하는 일이 명확하지 않습니다.

주요 문제는 클래스가 자체 기본 클래스라는 데 있습니다. 첫째, 클래스가 __class__속성 에만 존재하는 동일한 이름을 가진 거의 동일한 클래스의 서브 클래스가되는 것이 이상하지 않습니까? 이것은 또한 사용자가 정의 할 수 없음을 의미 자신의 기본 클래스에 같은 이름의 메소드를 호출 어떤 방법 으로 super()그들이 재귀 때문입니다. 이것은 클래스가 사용자 정의 __new__할 수 없으며 __init__호출 해야하는 클래스에서 파생 될 수 없음 을 의미합니다.

싱글 톤 패턴을 사용하는 경우

유스 케이스는 싱글 톤을 사용하려는 더 좋은 예 중 하나입니다 . 당신은 의견 중 하나에서 “나에게 로깅은 항상 싱글 톤의 자연스러운 후보로 보였습니다.”라고 말합니다. 당신은 절대적으로 맞습니다 .

사람들이 싱글 톤이 나쁘다고 말할 때 가장 일반적인 이유는 암시 적 공유 상태 이기 때문입니다 . 전역 변수와 최상위 모듈 가져 오기가 명시 적으로 공유 된 상태 인 반면 전달되는 다른 객체는 일반적으로 인스턴스화됩니다. 이것은 두 가지 예외 가 있지만 좋은 지적 입니다.

여러 곳에서 언급되는 첫 번째는 싱글 톤이 일정 할 때 입니다. 전역 상수, 특히 열거 형의 사용은 널리 받아 들여지고 어떤 사용자도 다른 사용자를 위해 그들을 엉망으로 만들 수 없기 때문에 제정신으로 간주됩니다 . 상수 싱글 톤에서도 마찬가지입니다.

덜 언급되는 두 번째 예외는 반대입니다. 싱글 톤이 데이터 소스가 아닌 데이터 싱크 일 경우 (직접 또는 간접). 이것이 로거가 싱글 톤을 “자연스럽게”사용하는 것처럼 느끼는 이유입니다. 다양한 사용자가 다른 사용자가 관심을 갖는 방식으로 로거변경 하지 않기 때문에 실제로 공유 된 상태없습니다 . 이것은 싱글 톤 패턴에 대한 주요 논증을 무시하고 , 작업에 대한 사용 편의성 때문에 합리적인 선택 입니다.

다음은 http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html 의 인용문입니다 .

자, 싱글 톤의 한 종류가 있습니다. 이는 도달 가능한 모든 객체가 변경 불가능한 싱글 톤입니다. 모든 객체가 불변 인 경우 Singleton은 전역 상태가 없으므로 모든 것이 일정합니다. 그러나 이런 종류의 싱글 톤을 변경 가능한 것으로 바꾸는 것은 매우 쉽습니다. 매우 미끄러운 경사입니다. 그러므로 나는이 싱글 톤들에 대해서도 반대한다. 왜냐하면 그것들이 나쁘기 때문이 아니라 그들이 나빠지 기가 매우 쉽다는 점이다. 참고로 Java 열거는 이러한 종류의 싱글 톤입니다. 열거에 상태를 넣지 않는 한 괜찮습니다. 그렇지 마십시오.

세미-허용되는 다른 종류의 싱글 톤은 코드의 실행에 영향을 미치지 않는 것입니다. “부작용”은 없습니다. 로깅은 완벽한 예입니다. 싱글 톤과 전역 상태로로드됩니다. 주어진 로거의 사용 가능 여부에 관계없이 응용 프로그램이 다르게 동작하지 않기 때문에 허용됩니다 (손상되지 않음). 여기에있는 정보는 한 가지 방법으로 흐릅니다. 응용 프로그램에서 로거로. 로거에서 응용 프로그램으로 정보가 흐르지 않기 때문에 로거가 전역 상태라고 생각하더라도 로거는 허용됩니다. 테스트 결과 무언가가 기록되고 있다고 주장하려면 로거를 계속 주입해야하지만 일반적으로 로거는 상태가 가득 차 있어도 유해하지 않습니다.


답변

class Foo(object):
     pass

some_global_variable = Foo()

모듈은 한 번만 가져 오며 다른 모든 항목은 지나치게 생각합니다. 싱글 톤을 사용하지 말고 전역을 사용하지 마십시오.


답변

모듈을 사용하십시오. 한 번만 가져옵니다. 그 안에 일부 전역 변수를 정의하십시오-단일 변수의 ‘속성’이됩니다. 싱글 톤의 ‘메서드’기능을 추가하십시오.


답변

파이썬에서는 싱글 톤이 필요하지 않을 것입니다. 모듈에서 모든 데이터와 함수를 정의하기 만하면 사실상 단일 항목이 있습니다.

정말로 싱글 톤 클래스를 가져야한다면 다음과 같이 갈 것입니다.

class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

쓰다:

from mysingleton import my_singleton
my_singleton.foo()

여기서 mysingleton.py는 My_Singleton이 정의 된 파일 이름입니다. 파일을 처음 가져온 후 Python은 코드를 다시 실행하지 않기 때문에 작동합니다.


답변

여기 하나의 라이너가 있습니다.

singleton = lambda c: c()

사용 방법은 다음과 같습니다.

@singleton
class wat(object):
    def __init__(self): self.x = 1
    def get_x(self): return self.x

assert wat.get_x() == 1

객체가 간절히 인스턴스화됩니다. 이것은 당신이 원하는 것일 수도 아닐 수도 있습니다.


답변

스택 오버플로 질문을 확인하십시오 . 파이썬에서 싱글 톤을 정의하는 간단하고 우아한 방법이 있습니까? 여러 솔루션으로.

파이썬에서 디자인 패턴에 대한 Alex Martelli의 이야기를 보길 강력히 권장합니다 : 1 2 부 . 특히, 1 부에서 그는 싱글 톤 / 공유 상태 객체에 대해 이야기합니다.


답변

여기 내 싱글 톤 구현이 있습니다. 수업을 꾸미기 만하면됩니다. 싱글 톤을 얻으려면 Instance메서드 를 사용해야합니다 . 예를 들면 다음과 같습니다.

   @Singleton
   class Foo:
       def __init__(self):
           print 'Foo created'

   f = Foo() # Error, this isn't how you get the instance of a singleton

   f = Foo.Instance() # Good. Being explicit is in line with the Python Zen
   g = Foo.Instance() # Returns already created instance

   print f is g # True

그리고 여기 코드가 있습니다 :

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)