Programing

Python에서 싱글 톤 만들기

crosscheck 2020. 9. 29. 07:18
반응형

Python에서 싱글 톤 만들기


이 질문은 싱글 톤 디자인 패턴 이 바람직한 지, 반 패턴인지 또는 종교 전쟁인지에 대한 논의가 아니라이 패턴이 가장 비단뱀적인 방식으로 파이썬에서 어떻게 가장 잘 구현되는지 논의하기위한 것입니다. 이 경우 '가장 비단뱀 적'이라는 것은 '최소 경악의 원칙'을 따른다는 의미로 정의 합니다.

싱글 톤이되는 여러 클래스가 있습니다 (내 유스 케이스는 로거 용이지만 중요하지 않습니다). 단순히 상속하거나 장식 할 수있을 때 고무줄을 추가하여 여러 클래스를 어지럽히고 싶지 않습니다.

최선의 방법 :


방법 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__.

메타 클래스에 대한 몇 마디. 메타 클래스는 클래스의 클래스입니다 . 즉, 클래스는 해당 메타 클래스인스턴스입니다 . 파이썬에서 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 은이 테스트에 실패합니다.

Python 2 및 3 호환 버전

Python2와 3 모두에서 작동하는 것을 작성하려면 약간 더 복잡한 체계를 사용해야합니다. 메타 클래스 유형의 서브 클래스가 일반적이기 때문에 type, 그것은 동적으로 메타 클래스로와 실행시에 중간 기본 클래스를 만든 다음 사용 하나를 사용하는 것이 가능하다 그런 대중의의 base class로서 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__호출 해야하는 클래스에서 파생 될 수 없습니다 .

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

귀하의 사용 사례는 싱글 톤을 사용하려는 더 좋은 예 중 하나입니다 . 당신은 코멘트 중 하나에서 "내게 로깅은 항상 Singletons의 자연스러운 후보로 보였습니다."라고 말합니다. 당신이 절대적으로 옳습니다 .

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

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

덜 언급되는 두 번째 예외는 그 반대입니다. 싱글 톤이 데이터 소스 (직간접 적) 아니라 데이터 싱크입니다. 이것이 로거가 싱글 톤에 대해 "자연스러운"사용처럼 느끼는 이유입니다. 다양한 사용자가 다른 사용자가 신경 쓰는 방식으로 로거변경 하지 않기 때문에 실제로 공유 된 상태없습니다 . 이것은 싱글 톤 패턴에 대한 주요 주장을 무효화 하고 작업 에 사용 하기 쉽기 때문에 합리적인 선택으로 만듭니다 .

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

이제 괜찮은 Singleton이 있습니다. 그것은 도달 가능한 모든 객체가 불변하는 싱글 톤입니다. 모든 객체가 불변이면 모든 것이 일정하기 때문에 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

개체는 열심히 인스턴스화됩니다. 이것은 당신이 원하는 것일 수도 있고 아닐 수도 있습니다.


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

Python의 디자인 패턴에 대한 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)

방법 3은 매우 깔끔해 보이지만 프로그램이 Python 2Python 3 모두에서 실행되도록하려면 작동하지 않습니다. Python 3 버전은 Python 2에서 구문 오류를 제공하기 때문에 Python 버전 테스트로 별도의 변형을 보호하는 것조차 실패합니다.

Mike Watkins에게 감사드립니다 : http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ . 프로그램이 Python 2와 Python 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]

MC = Singleton('MC', (object), {})

class MyClass(MC):
    pass    # Code for the class implementation

할당의 '객체'를 'BaseClass'로 대체해야한다고 생각하지만 시도하지 않았습니다 (그림과 같이 코드를 시도했습니다).


글쎄, 모듈 수준의 전역을 갖는 것에 대한 일반적인 Pythonic 제안에 동의하는 것 외에는 어떨까요?

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *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(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

출력은 다음과 같습니다.

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

이것은 어떤가요:

def singleton(cls):
    instance=cls()
    cls.__new__ = cls.__call__= lambda cls: instance
    cls.__init__ = lambda self: None
    return instance

싱글 톤이어야하는 클래스의 데코레이터로 사용하십시오. 이렇게 :

@singleton
class MySingleton:
    #....

이것은 singleton = lambda c: c()다른 답변 데코레이터 와 유사합니다 . 다른 솔루션과 마찬가지로 유일한 인스턴스에는 클래스 이름 ( MySingleton)이 있습니다. 그러나이 솔루션을 사용하면을 수행하여 클래스에서 인스턴스를 "생성"할 수 있습니다 (실제로 유일한 인스턴스를 가져옴) MySingleton(). 또한 수행하여 추가 인스턴스를 만들지 못하게합니다 type(MySingleton)()(동일한 인스턴스도 반환 됨).


나는 내 것을 반지에 던질 것이다. 단순한 데코레이터입니다.

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

다른 솔루션에 비해 이점이 있다고 생각합니다.

  • 명확하고 간결합니다 (내 눈에는; D).
  • 그 동작은 완전히 캡슐화됩니다. .NET Framework 구현에 대해 한 가지 사항을 변경할 필요가 없습니다 YourClass. 여기에는 클래스에 메타 클래스를 사용할 필요가 없습니다 (위의 메타 클래스는 "실제"클래스가 아니라 공장에 있음).
  • 원숭이 패치에 의존하지 않습니다.
  • 발신자에게 투명합니다.
    • 호출자는 여전히 단순히 import YourClass, 클래스처럼 보이며 정상적으로 사용합니다. 발신자를 공장 기능에 맞게 조정할 필요가 없습니다.
    • 무엇 YourClass()인스턴스화하는 것은 아직도의 진정한 인스턴스 YourClass부작용의 기회가의 결과 없습니다, 그래서 당신은 어떤 종류의,하지 프록시를 구현했습니다.
    • isinstance(instance, YourClass) 유사한 작업은 여전히 ​​예상대로 작동합니다 (이 비트에는 abc가 필요하므로 Python <2.6은 제외됩니다).

한 가지 단점이 있습니다. 실제 클래스의 클래스 메서드와 정적 메서드는 팩토리 클래스를 통해 투명하게 호출 할 수 없습니다. 필자는 이것을 거의 사용하지 않아서 그 필요성에 부딪힌 적이 없었지만 __getattr__()실제 클래스에 대한 모든 속성 액세스를 위임 하도록 구현하는 공장에서 사용자 지정 메타 클래스를 사용하면 쉽게 수정할 수 있습니다 .

내가 실제로 더 유용하다고 찾은 관련 패턴 (이러한 종류의 항목이 매우 자주 필요하다고 말하는 것은 아님)은 동일한 인수로 클래스를 인스턴스화 하면 동일한 인스턴스를 다시 가져 오는 "고유 한"패턴 입니다. 즉 "인수 당 단일"입니다. 위의 내용은이 우물에 적용되어 더욱 간결 해집니다.

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

모든 말은, 나는 당신이 이러한 것 중 하나가 필요하다고 생각한다면, 아마 잠시 멈추고 당신이 정말로 필요한지 스스로에게 물어봐야한다는 일반적인 조언에 동의합니다. 99 %의 시간, YAGNI.


Tolli의 답변을 기반으로 한 코드 .

#decorator, modyfies new_cls
def _singleton(new_cls):
    instance = new_cls()                                              #2
    def new(cls):
        if isinstance(instance, cls):                                 #4
            return instance
        else:
            raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
    new_cls.__new__  = new                                            #3
    new_cls.__init__ = lambda self: None                              #5
    return new_cls


#decorator, creates new class
def singleton(cls):
    new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
    return _singleton(new_cls)


#metaclass
def meta_singleton(name, bases, attrs):
    new_cls = type(name, bases, attrs)                                #1
    return _singleton(new_cls)

설명:

  1. 주어진에서 상속하는 새 클래스를 만듭니다 cls
    ( cls예를 들어 누군가가 원하는 경우 수정하지 않음 singleton(list))

  2. 인스턴스를 만듭니다. 재정의하기 전에 __new__너무 쉽습니다.

  3. 이제 인스턴스를 쉽게 만들었을 때 방금 __new__전에 정의한 메서드를 사용하여 재정의 합니다.
  4. 이 함수는 instance호출자가 기대하는 값일 때만 반환 하고, 그렇지 않으면 TypeError.
    누군가 데코 레이팅 된 클래스에서 상속을 시도하면 조건이 충족되지 않습니다.

  5. 경우 __new__()반환의 인스턴스 cls, 새로운 인스턴스의 __init__()메소드가 호출 될 것 같은 __init__(self[, ...])자기 새 인스턴스이고, 나머지 인수에 전달 된 동일합니다 __new__().

    instance이미 초기화되었으므로 함수는 __init__아무것도하지 않는 함수로 대체 됩니다.

온라인으로 작동하는지 확인


fab의 답변과 약간 비슷하지만 정확히 동일하지는 않습니다.

단일 계약은 우리가 생성자를 여러 번 호출 할 수 필요가 없습니다. 싱글 톤은 한 번만 생성되어야하므로 한 번만 생성되는 것으로보아야하지 않습니까? 생성자를 "스푸핑"하면 가독성이 떨어집니다.

그래서 내 제안은 다음과 같습니다.

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

이것은 instance사용자 코드에 의한 생성자 또는 필드의 사용을 배제하지 않습니다 .

if Elvis() is King.instance:

... Elvis아직 생성되지 않았다는 것을 확실히 알고 있다면 King.

그러나 사용자 가이 방법을 보편적 으로 사용 하도록 권장 합니다 the.

Elvis.the().leave(Building.the())

이 당신은 또한 무시할 수있는 완전한 만들려면 __delattr__()시도가 삭제하려 할 경우 예외를 인상 instance하고 재정의 __del__()예외를 제기하도록 (우리는 프로그램이 종료 알지 못한다면 ...)

추가 개선


코멘트와 편집을 도와 주신 분들께 감사 드리며, 그 중 더 많은 것을 환영합니다. 내가 Jython을 사용하는 동안 이것은 더 일반적으로 작동하고 스레드로부터 안전합니다.

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()

        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance

    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

참고 사항 :

  1. python2.x의 객체에서 하위 클래스를 사용하지 않으면 사용하지 않는 구식 클래스를 얻게됩니다. __new__
  2. 데코레이션 __new__때는 @classmethod로 데코레이션해야합니다. 그렇지 않으면 __new__언 바운드 인스턴스 메소드가됩니다.
  3. 이것은 메타 클래스를 사용하여 개선 될 수 있습니다. 이렇게 the하면 클래스 수준 속성 을 만들고 이름을 다음과 같이 바꿀 수 있습니다.instance

라이너 1 개 (자랑 스럽지는 않지만 작업을 수행합니다) :

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify

Singleton 인스턴스의 지연 초기화가 필요하지 않은 경우 다음은 쉽고 스레드로부터 안전해야합니다.

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

이 방법 A은 모듈 가져 오기에서 초기화되는 싱글 톤입니다.


  • 동일한 클래스의 인스턴스를 여러 개 갖고 싶지만 args 또는 kwargs가 다른 경우에만 이것을 사용할 수 있습니다.
  • 전의.
    1. serial통신을 처리하는 클래스가 있고 직렬 포트를 인수로 보내려는 인스턴스를 만들려면 기존 접근 방식이 작동하지 않습니다.
    2. 위에서 언급 한 데코레이터를 사용하면 인수가 다른 경우 클래스의 여러 인스턴스를 만들 수 있습니다.
    3. 동일한 인수에 대해 데코레이터는 이미 생성 된 동일한 인스턴스를 반환합니다.
>>> from decorators import singleton
>>>
>>> @singleton
... class A:
...     def __init__(self, *args, **kwargs):
...         pass
...
>>>
>>> a = A(name='Siddhesh')
>>> b = A(name='Siddhesh', lname='Sathe')
>>> c = A(name='Siddhesh', lname='Sathe')
>>> a is b  # has to be different
False
>>> b is c  # has to be same
True
>>>

싱글 톤 패턴을 이해하지 못했을 수도 있지만 내 솔루션은 간단하고 실용적입니다 (pythonic?). 이 코드는 두 가지 목표를 충족합니다.

  1. Foo어디서나 액세스 할 수 있는 인스턴스 (전역)를 만듭니다.
  2. 의 인스턴스는 하나만 Foo존재할 수 있습니다.

이것이 코드입니다.

#!/usr/bin/env python3

class Foo:
    me = None

    def __init__(self):
        if Foo.me != None:
            raise Exception('Instance of Foo still exists!')

        Foo.me = self


if __name__ == '__main__':
    Foo()
    Foo()

산출

Traceback (most recent call last):
  File "./x.py", line 15, in <module>
    Foo()
  File "./x.py", line 8, in __init__
    raise Exception('Instance of Foo still exists!')
Exception: Instance of Foo still exists!

이 솔루션을 어디서 찾았는지 기억할 수 없지만 Python 전문가가 아닌 관점에서 볼 때 가장 '우아한'것으로 나타났습니다.

class SomeSingleton(dict):
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if SomeSingleton.__instance__ is None:
            SomeSingleton.__instance__ = dict.__new__(cls)
        return SomeSingleton.__instance__

    def __init__(self):
        pass

    def some_func(self,arg):
        pass

내가 왜 이것을 좋아합니까? 데코레이터도, 메타 클래스도, 다중 상속도없고 ... 더 이상 싱글 톤이되고 싶지 않다면 __new__메서드를 삭제하면 됩니다. 내가 Python (및 일반적으로 OOP)을 처음 접했기 때문에 누군가 이것이 왜 이것이 끔찍한 접근 방식인지에 대해 곧바로 설명해 줄 것이라고 기대합니다.


이것이 내가 선호하는 싱글 톤 구현 방법입니다.

class Test(object):
    obj = None

    def __init__(self):
        if Test.obj is not None:
            raise Exception('A Test Singleton instance already exists')
        # Initialization code here

    @classmethod
    def get_instance(cls):
        if cls.obj is None:
            cls.obj = Test()
        return cls.obj

    @classmethod
    def custom_method(cls):
        obj = cls.get_instance()
        # Custom Code here

이 답변은 귀하가 찾고있는 것이 아닐 수 있습니다. 비교를 위해 그 객체 만이 정체성을 가지고 있다는 의미에서 싱글 톤을 원했습니다. 제 경우에는 Sentinel Value 로 사용되었습니다 . 대답은 매우 간단합니다. 어떤 객체 든 만들고 mything = object()파이썬의 본성에 따라 그 것만이 정체성을 갖게됩니다.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration

이 솔루션은 모듈 수준에서 약간의 네임 스페이스 오염을 유발하지만 (단지 하나가 아닌 세 가지 정의) 따르기 쉽습니다.

나는 이와 같은 것을 작성할 수 있기를 원하지만 (지연 초기화) 불행히도 클래스는 자체 정의 본문에서 사용할 수 없습니다.

# wouldn't it be nice if we could do this?
class Foo(object):
    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = object()
            cls.instance.__class__ = Foo
        return cls.instance

불가능하므로 초기화 및 정적 인스턴스를

빠른 초기화 :

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        return foo_instance


foo_instance = FooMaker()
foo_instance.__class__ = Foo

지연 초기화 :

빠른 초기화 :

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        global foo_instance
        if foo_instance is None:
            foo_instance = FooMaker()
        return foo_instance


foo_instance = None

참고 URL : https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python

반응형