etc./StackOverFlow

파이썬에서 싱글톤 만들기

청렴결백한 만능 재주꾼 2023. 4. 30. 00:07
반응형

질문자 :theheadofabroom


이 질문은 싱글톤 디자인 패턴 이 바람직한지, 안티 패턴인지, 종교 전쟁에 대한 논의가 아니라 가장 파이썬적인 방식으로 이 패턴을 파이썬에서 가장 잘 구현하는 방법을 논의하기 위한 것입니다. 이 예에서 나는 '가장 비단결적'이라고 정의하여 그것이 '가장 놀라움의 원칙'을 따른다는 것을 의미합니다 .

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

최선의 방법:


방법 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 자체는 클래스가 아닌 함수이므로 클래스 메서드를 호출할 수 없습니다. 또한
 x = MyClass(); y = MyClass(); t = type(n)();

x == y 이지만 x != t && y != t


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

장점

  • 진짜 수업이다

단점

  • 다중 상속 - 으악! 두 번째 기본 클래스에서 상속하는 동안 __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)

Singleton.__call__if 문에.

메타클래스에 대한 몇 마디. 메타클래스는 클래스의 클래스입니다 . 즉, 클래스는 해당 메타클래스 의 인스턴스입니다. type(obj) 사용하여 객체의 메타클래스를 찾습니다. 일반적인 새 스타일 클래스는 type type 입니다. Logger 위의 코드는 형식이 될 것입니다 class 'your_module.Singleton' 의 (만) 예처럼, Logger 형식이 될 것입니다 class 'your_module.Logger' . Logger() 하여 logger를 호출하면 Logger 의 메타클래스인 Singleton 에 무엇을 해야 하는지 묻고 인스턴스 생성을 선점할 수 있습니다. myclass.attribute 를 수행하여 속성 중 하나를 참조할 때 __getattr__ 을 호출하여 클래스에 무엇을 해야 하는지 묻는 것과 동일합니다.

메타클래스는 본질적으로 클래스 의 정의가 의미하는 것과 그 정의 를 구현하는 방법을 결정합니다. 예를 들어 http://code.activestate.com/recipes/498149/ 를 참조하세요. 이는 본질적으로 메타클래스를 사용하여 Python에서 struct 스레드 메타 클래스에 대한 몇 가지 (구체적인) 사용 사례는 무엇입니까? 또한 몇 가지 예를 제공하지만 일반적으로 특히 ORM에서 사용되는 선언적 프로그래밍과 관련된 것으로 보입니다.

이 상황에서 Method #2 를 사용하고 하위 클래스가 __new__ 메서드를 SubClassOfSingleton() 을 호출할 때마다 실행됩니다. 왜냐하면 저장된 인스턴스를 반환하는 메서드 호출을 담당하기 때문입니다. 메타 클래스를 사용하면 유일한 인스턴스가 생성될 때 한 번만 호출됩니다. 유형에 따라 결정되는 클래스 호출의 의미 를 사용자 정의하려고 합니다.

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

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

또한 싱글톤 패턴이 "단일 책임 원칙"을 위반한다는 말을 듣게 될 것입니다. 각 클래스는 한 가지만 수행해야 합니다. 그렇게 하면 코드가 분리되고 캡슐화되기 때문에 다른 코드를 변경해야 할 때 코드가 수행하는 작업을 엉망으로 만드는 것에 대해 걱정할 필요가 없습니다. 메타클래스 구현 은 이 테스트를 통과합니다 . 메타클래스는 패턴을 적용할 책임이 있으며 생성된 클래스와 서브클래스 는 그들이 싱글톤이라는 것을 알 필요가 없습니다. 메소드 #1 은 "MyClass 자체는 클래스가 아닌 함수이므로 클래스 메소드를 호출할 수 없습니다."에서 언급했듯이 이 테스트에 실패합니다.

Python 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 의 인용문입니다.

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

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


agf

class Foo(object): pass some_global_variable = Foo()

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


Cat Plus Plus

모듈을 사용합니다. 한 번만 수입됩니다. 여기에 일부 전역 변수를 정의하십시오. 이는 싱글톤의 '속성'이 됩니다. 일부 기능을 추가하십시오 - 싱글톤의 '메서드'.


warvariuc

아마도 Python에서는 싱글톤이 필요하지 않을 것입니다. 모듈에서 모든 데이터와 기능을 정의하기만 하면 사실상 싱글톤이 됩니다.

 import datetime file_name=None def set_file_name(new_file_name: str): global file_name file_name=new_file_name def write(message: str): global file_name if file_name: with open(file_name, 'a+') as f: f.write("{} {}\n".format(datetime.datetime.now(), message)) else: print("LOG: {}", message)

사용:

 import log log.set_file_name("debug.log") log.write("System starting") ...

정말 절대적으로 싱글 톤 클래스가 있어야한다면 다음과 같이 갈 것입니다.

 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이 코드를 다시 실행하지 않기 때문에 작동합니다.


Alan Dyke

다음은 한 줄짜리입니다.

 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

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


Jonas Kölker

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

Python의 디자인 패턴에 대한 Alex Martelli의 강연을 시청하는 것이 좋습니다: 1 부 및 2부 . 특히 1부에서 그는 싱글톤/공유 상태 객체에 대해 이야기합니다.


Anton

  • 동일한 클래스의 인스턴스를 여러 개 갖고 싶지만 args 또는 kwargs가 다른 경우에만 타사 파이썬 패키지 Handy Decorators (패키지 decorators )를 사용할 수 있습니다.
  • 전.
    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 >>>

Siddhesh Suhas Sathe

다음은 싱글톤을 직접 구현한 것입니다. 수업을 장식하기만 하면 됩니다. 싱글톤을 얻으려면 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)

Paul Manta

방법 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'로 대체해야 한다고 가정하지만 시도하지 않았습니다(그림과 같이 코드를 시도했습니다).


Tim

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

 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).
  • 그 동작은 완전히 캡슐화됩니다. YourClass 구현에 대해 단 한 가지도 변경할 필요가 없습니다. 여기에는 클래스에 대한 메타클래스를 사용할 필요가 없다는 것이 포함됩니다(위의 메타클래스는 "실제" 클래스가 아니라 공장에 있음).
  • 원숭이 패치에 의존하지 않습니다.
  • 발신자에게 투명합니다.
    • 호출자는 여전히 YourClass 를 가져오기만 하면 클래스처럼 보이고(그렇기 때문에) 정상적으로 사용합니다. 호출자를 팩토리 함수에 적응시킬 필요가 없습니다.
    • YourClass() 인스턴스화하는 것은 여전히 YourClass 의 실제 인스턴스이므로 이로 인해 부작용이 발생할 가능성은 없습니다.
    • isinstance(instance, YourClass) 및 유사한 작업은 여전히 예상대로 작동합니다(이 비트에는 abc가 필요하므로 Python <2.6을 제외함).

한 가지 단점은 나에게 발생합니다. 실제 클래스의 classmethods와 staticmethods는 그것을 숨기는 팩토리 클래스를 통해 투명하게 호출할 수 없습니다. 나는 이것을 거의 사용하지 않아서 그 필요성이 발생하지 않았지만 모든 속성 액세스를 실제 클래스에 위임하기 위해 __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.


mikenerone

function 속성을 사용하는 것도 매우 간단합니다.

 def f(): if not hasattr(f, 'value'): setattr(f, 'value', singletonvalue) return f.value

Ruben Decrop

나는 매우 명확하고 직관적인 이 솔루션을 선호합니다. 예를 들어 다른 스레드가 이미 만든 경우 이중 확인을 사용하고 있습니다. 고려해야 할 추가 사항은 역직렬화가 다른 인스턴스를 생성하지 않는지 확인하는 것입니다. https://gist.github.com/werediver/4396488

 import threading # Based on tornado.ioloop.IOLoop.instance() approach. # See https://github.com/facebook/tornado class SingletonMixin(object): __singleton_lock = threading.Lock() __singleton_instance = None @classmethod def instance(cls): if not cls.__singleton_instance: with cls.__singleton_lock: if not cls.__singleton_instance: cls.__singleton_instance = cls() return cls.__singleton_instance if __name__ == '__main__': class A(SingletonMixin): pass class B(SingletonMixin): pass a, a2 = A.instance(), A.instance() b, b2 = B.instance(), B.instance() assert a is a2 assert b is b2 assert a is not b print('a: %s\na2: %s' % (a, a2)) print('b: %s\nb2: %s' % (b, b2))

Andrei R.

글쎄, 모듈 수준 전역을 갖는 것에 대한 일반적인 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

Guard

이것은 어떤가요:

 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)() 을 수행하여 추가 인스턴스를 생성하는 것을 방지합니다(동일한 인스턴스를 반환함).


Tolli

메타 클래스를 사용하는 우아한 솔루션을 추천합니다.

 class Singleton(type): # Inherit from "type" in order to gain access to method __call__ def __init__(self, *args, **kwargs): self.__instance = None # Create a variable to store the object reference super().__init__(*args, **kwargs) def __call__(self, *args, **kwargs): if self.__instance is None: # if the object has not already been created self.__instance = super().__call__(*args, **kwargs) # Call the __init__ method of the subclass (Spam) and save the reference return self.__instance else: # if object (Spam) reference already exists; return it return self.__instance class Spam(metaclass=Singleton): def __init__(self, x): print('Creating Spam') self.x = x if __name__ == '__main__': spam = Spam(100) spam2 = Spam(200)

산출:

 Creating Spam

출력에서 볼 수 있듯이 하나의 개체만 인스턴스화됩니다.


Dorcioman

우연히 간단하게 만들어서 공유해보려고 합니다...

 class MySingleton(object): def __init__(self, *, props={}): self.__dict__ = props mything = MySingleton() mything.test = 1 mything2 = MySingleton() print(mything2.test) mything2.test = 5 print(mything.test)

byteface

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 에서 상속하여 새 클래스 만들기
    (누군가가 예를 들어 singleton(list) cls 를 수정하지 않음)

  2. 인스턴스를 생성합니다. __new__ 재정의하기 전에 매우 쉽습니다.

  3. 이제 인스턴스를 쉽게 생성하면 방금 전에 정의한 메서드를 사용하여 __new__
  4. 이 함수는 호출자가 예상한 instance TypeError 시킵니다.
    누군가가 장식된 클래스에서 상속을 시도하면 조건이 충족되지 않습니다.

  5. __new__() cls 의 인스턴스를 반환하면 새 인스턴스의 __init__() 메서드가 __init__(self[, ...]) 처럼 호출됩니다. 여기서 self는 새 인스턴스이고 나머지 인수는 전달된 것과 동일합니다. __new__() .

    instance 는 이미 초기화되었으므로 함수는 __init__ 를 아무 작업도 수행하지 않는 함수로 바꿉니다.

온라인에서 작동하는 모습 보기


GingerPlusPlus

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 를 삭제하려는 경우 Exception 을 발생시키고 __del__() 재정의하여 Exception을 발생시킬 수 있습니다(프로그램이 종료된다는 것을 알지 못하는 한...)

추가 개선 사항


댓글과 수정에 도움을 주신 분들께 감사드립니다. 더 많은 것을 환영합니다. 내가 자이썬을 사용하는 동안 이것은 더 일반적으로 작동하고 스레드로부터 안전해야 합니다.

 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

mike rodent

하나의 라이너(나는 자랑스럽지 않지만 그것은 일을 한다):

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

polvoazul

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

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

이런 식으로 A 는 모듈 가져오기에서 초기화된 싱글톤입니다.


Serge Rogatch

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

  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!

buhtz

이 문제로 한동안 고군분투한 후에 결국 다음과 같은 방법을 생각해 냈으므로 별도의 모듈에서 호출할 때 구성 개체가 한 번만 로드되도록 했습니다. 메타클래스를 사용하면 전역 클래스 인스턴스를 builtins dict에 저장할 수 있습니다. 현재로서는 적절한 프로그램 전역을 저장하는 가장 깔끔한 방법으로 보입니다.

 import builtins # ----------------------------------------------------------------------------- # So..... you would expect that a class would be "global" in scope, however # when different modules use this, # EACH ONE effectively has its own class namespace. # In order to get around this, we use a metaclass to intercept # "new" and provide the "truly global metaclass instance" if it already exists class MetaConfig(type): def __new__(cls, name, bases, dct): try: class_inst = builtins.CONFIG_singleton except AttributeError: class_inst = super().__new__(cls, name, bases, dct) builtins.CONFIG_singleton = class_inst class_inst.do_load() return class_inst # ----------------------------------------------------------------------------- class Config(metaclass=MetaConfig): config_attr = None @classmethod def do_load(cls): ...<load-cfg-from-file>...

Den-Jason

장점

진정한 클래스입니다. 자동으로 상속을 커버합니다. 적절한 목적을 위해 메타 클래스를 사용합니다.

거기 아무도 없나요?

이것은 직렬화에 문제가 될 것입니다. 파일(피클)에서 객체를 역직렬화하려고 하면 __call__ 사용하지 않으므로 새 파일을 생성합니다. 이를 방지하기 위해 __new__ 와 함께 기본 클래스 상속을 사용할 수 있습니다.


Vintage

instance 를 속성으로 사용하려는 경우 metaclass 를 사용할 수 있습니다. 예를 들어;

 class SingletonMeta(type): def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) cls._instance = None cls._locker = threading.Lock() @property def instance(self, *args, **kwargs): if self._instance is None: with self._locker: if self._instance is None: self._instance = self(*args, **kwargs) return self._instance class MyClass(metaclass=SingletonMeta): def __init__(self): # init here pass # get the instance my_class_instance = MyClass.instance

omerfarukdogan

또한 메타 클래스에서 파생되는 데코레이터 구문을 선호합니다. 내 2센트:

 from typing import Callable, Dict, Set def singleton(cls_: Callable) -> type: """ Implements a simple singleton decorator """ class Singleton(cls_): # type: ignore __instances: Dict[type, object] = {} __initialized: Set[type] = set() def __new__(cls, *args, **kwargs): if Singleton.__instances.get(cls) is None: Singleton.__instances[cls] = super().__new__(cls, *args, **kwargs) return Singleton.__instances[cls] def __init__(self, *args, **kwargs): if self.__class__ not in Singleton.__initialized: Singleton.__initialized.add(self.__class__) super().__init__(*args, **kwargs) return Singleton @singleton class MyClass(...): ...

이것은 제공되는 다른 데코레이터보다 몇 가지 이점이 있습니다.

  • isinstance(MyClass(), MyClass) 는 여전히 작동합니다(클래스 대신 절에서 함수를 반환하면 isinstance가 실패함)
  • property , classmethodstaticmethod 는 여전히 예상대로 작동합니다.
  • __init__() 생성자는 한 번만 실행됩니다.
  • @singleton을 다시 사용하여 장식된 클래스(쓸모없나요?)에서 상속할 수 있습니다.

단점:

  • print(MyClass().__class__.__name__) MyClass 대신 Singleton 을 반환합니다. 그래도 필요한 경우 위에서 제안한 대로 메타클래스를 사용하는 것이 좋습니다.

생성자 매개변수를 기반으로 하는 다른 인스턴스가 필요한 경우 이 솔루션을 개선해야 합니다( siddhesh-suhas-sathe에서 제공하는 솔루션이 이를 제공함).

마지막으로, 다른 사람이 제안한 것처럼 파이썬에서 모듈을 사용하는 것을 고려하십시오. 모듈 객체입니다. 변수에 전달하고 다른 클래스에 주입할 수도 있습니다.


Boriel

이 솔루션을 어디에서 찾았는지 기억이 나지 않지만 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)을 처음 접할 때 이것이 왜 끔찍한 접근 방식인지에 대해 누군가가 나에게 바로잡아 줄 것이라고 기대합니다.


2cynykyl

이 답변은 당신이 찾고 있는 것이 아닐 가능성이 높습니다. 비교를 위해 해당 개체에만 고유한 ID가 있다는 의미에서 싱글톤을 원했습니다. 제 경우에는 Sentinel Value 로 사용되었습니다. 대답은 매우 간단합니다. 모든 객체를 mything = object() 만들고 파이썬의 특성에 따라 해당 객체만 고유한 ID를 갖습니다.

 #!python MyNone = object() # The singleton for item in my_list: if item is MyNone: # An Example identity comparison raise StopIteration

ThorSummoner

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

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

 # 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

Community Wiki

출처 : http:www.stackoverflow.com/questions/6760685/creating-a-singleton-in-python

반응형