질문 : Python에서 싱글 톤 만들기
이 질문은 싱글 톤 디자인 패턴 이 바람직한 지, 반 패턴인지 또는 종교 전쟁인지에 대한 논의가 아니라이 패턴이 가장 비단뱀적인 방식으로 파이썬에서 어떻게 가장 잘 구현되는지 논의하기위한 것입니다. 이 경우 '가장 비단뱀 적'이라는 것은 '최소 경악의 원칙'을 따른다는 의미로 정의 합니다.
싱글 톤이되는 여러 클래스가 있습니다 (내 사용 사례는 로거 용이지만 중요하지 않습니다). 단순히 상속하거나 장식 할 수있을 때 고무줄을 추가하여 여러 클래스를 어지럽히고 싶지 않습니다.
최선의 방법 :
방법 1: A decorator
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: 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
장점
- 진정한 클래스 입니다
단점
- 다중 상속-eugh!
__new__
는 두 번째 기본 클래스에서 상속하는 동안 덮어 쓸 수 있습니까? 필요한 것 이상을 생각해야합니다.
방법 3 : 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
장점
- 진정한 클래스 입니다
- 상속을 자동으로 처리
__metaclass__
를 사용합니다 (그리고 그것을 알게했습니다).
단점
- 거기 아무도 없나요?
방법 4: 같은 이름의 클래스를 반환하는 decorator
This question is not for the discussion of whether or not the singleton design pattern is desirable, is an anti-pattern, or for any religious wars, but to discuss how this pattern is best implemented in Python in such a way that is most pythonic. In this instance I define 'most pythonic' to mean that it follows the 'principle of least astonishment'.
I have multiple classes which would become singletons (my use-case is for a logger, but this is not important). I do not wish to clutter several classes with added gumph when I can simply inherit or decorate.
Best methods:
Method 1: A decorator
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
Pros
Decorators are additive in a way that is often more intuitive than multiple inheritance.
Cons
While objects created using MyClass() would be true singleton objects, MyClass itself is a a function, not a class, so you cannot call class methods from it. Also for
x = MyClass();
y = MyClass();
t = type(n)();
then x == y but x != t && y != t
Method 2: 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
Pros
It's a true class
Cons
Multiple inheritance - eugh! __new__ could be overwritten during inheritance from a second base class? One has to think more than is necessary.
Method 3: 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
Pros
It's a true class
Auto-magically covers inheritance
Uses __metaclass__ for its proper purpose (and made me aware of it)
Cons
Are there any?
Method 4: decorator returning a class with the same name
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
입니다. Logger
위의 코드는 형식이 될 것입니다 class 'your_module.Singleton'
의 (만) 예처럼, Logger
형식이 될 것입니다 class 'your_module.Logger'
. Logger()
logger를 호출하면 Logger
의 메타 클래스 인 Singleton
에 무엇을해야하는지 묻고 인스턴스 생성을 선점 할 수 있습니다. myclass.attribute
를 수행하여 속성 중 하나를 참조 할 때 __getattr__
을 호출하여 클래스에 무엇을해야하는지 묻는 Python과 동일합니다.
메타 클래스는 기본적 으로 클래스 정의의 의미 와 해당 정의를 구현하는 방법을 결정합니다. 예를 들어 http://code.activestate.com/recipes/498149/를 참조하십시오. 이것은 본질적으로 메타 클래스를 사용하여 Python에서 struct
메타 클래스의 (구체적인) 사용 사례는 무엇입니까? 또한 몇 가지 예를 제공하며 일반적으로 특히 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()
를 사용하거나 recursing 을 사용해야하며, __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 열거 형은 이러한 종류의 싱글 톤일뿐입니다. 열거 형에 상태를 넣지 않는 한 괜찮습니다. 그러지 마십시오.)
반 허용 가능한 다른 종류의 싱글 톤은 코드 실행에 영향을주지 않는 것입니다. "부작용"이 없습니다. 로깅은 완벽한 예입니다. 싱글 톤 및 글로벌 상태로로드됩니다. 주어진 로거가 활성화되어 있는지 여부에 관계없이 응용 프로그램이 다르게 작동하지 않기 때문에 허용됩니다. 여기에있는 정보는 응용 프로그램에서 로거로 한 방향으로 흐릅니다. 로거에서 애플리케이션으로 정보가 흐르지 않기 때문에 로거가 글로벌 상태라고 생각하더라도 로거는 허용됩니다. 테스트에서 무언가가 기록되고 있다고 주장하도록하려면 여전히 로거를 삽입해야하지만 일반적으로 로거는 상태가 가득 차더라도 유해하지 않습니다.
출처 : https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python
'프로그래밍 언어 > Python' 카테고리의 다른 글
Python 애플리케이션에 가장 적합한 프로젝트 구조를 구성하는 좋은 방법 (0) | 2021.08.05 |
---|---|
Python의 C와 유사한 구조 (0) | 2021.08.03 |
Python에서 switch 문을 사용하는 방법 (0) | 2021.08.03 |
파이썬에서 "at"(@) 기호의 역할 (0) | 2021.07.30 |
DataFrame 행 순서를 섞는 방법 (0) | 2021.07.29 |