Python에서 싱글톤 모드를 구현하는 5가지 방법

WBOY
풀어 주다: 2023-05-26 22:31:24
앞으로
1526명이 탐색했습니다.

Python 实现单例模式的五种写法

싱글턴 패턴은 일반적으로 사용되는 소프트웨어 디자인 패턴입니다. 이 패턴의 주요 목적은 특정 클래스의 인스턴스가 하나만 존재하도록 하는 것입니다. 싱글톤 개체는 특정 클래스의 인스턴스 하나만 전체 시스템에 나타나도록 하려는 경우 유용합니다.

예를 들어 서버 프로그램의 구성 정보는 파일에 저장되며 클라이언트는 AppConfig 클래스를 통해 구성 파일 정보를 읽습니다. 프로그램을 실행하는 동안 구성 파일의 내용을 여러 위치에서 사용해야 하는 경우, 즉 AppConfig 개체의 인스턴스를 여러 위치에서 생성해야 하므로 여러 AppConfig 인스턴스 개체가 존재하게 됩니다. 이는 특히 구성 파일에 많은 콘텐츠가 포함된 경우 메모리를 심각하게 낭비하게 됩니다.

실제로 AppConfig와 같은 클래스의 경우 프로그램 실행 중에 인스턴스 개체가 하나만 존재하기를 바랍니다. Python에서는 싱글 톤 패턴을 구현하기 위해 다양한 방법을 사용할 수 있습니다.


모듈을 사용하여

    ecorators를 사용하는

  1. 를 사용하여 __new__ 방법을 기반으로합니다.
  2. 다음은 자세한 소개입니다.

  3. 모듈 사용

  4. 사실 Python 모듈은 자연스러운 싱글톤 모드입니다. 모듈을 처음 가져올 때와 가져올 때 .pyc 파일을 생성하기 때문입니다. 두 번째로, 모듈 코드를 다시 실행하지 않고 .pyc 파일이 직접 로드됩니다.


그래서 싱글톤 객체를 얻으려면 모듈에서 관련 함수와 데이터만 정의하면 됩니다.


정말로 싱글톤 클래스를 원한다면 다음을 고려해 볼 수 있습니다:

class Singleton(object):
 def foo(self):
 pass
singleton = Singleton()
로그인 후 복사

위 코드를 mysingleton.py 파일에 저장하세요. 사용하려면 이 파일의 개체를 다른 파일로 직접 가져오세요. 객체는 싱글턴 모드의 객체입니다

from mysingleton import singleton
로그인 후 복사

데코레이터 사용하기

def Singleton(cls):
 _instance = {}
 def _singleton(*args, **kargs):
 if cls not in _instance:
 _instance[cls] = cls(*args, **kargs)
 return _instance[cls]
 return _singleton
@Singleton
class A(object):
 a = 1
 def __init__(self, x=0):
 self.x = x
a1 = A(2)
a2 = A(3)
로그인 후 복사

클래스 사용하기

class Singleton(object):
 def __init__(self):
 pass
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
로그인 후 복사

일반적으로 다들 이걸로 싱글턴 모드가 완성됐다고 생각하지만, 멀티스레드를 사용하면 문제가 발생합니다:

class Singleton(object):
 def __init__(self):
 pass
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
import threading
def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
로그인 후 복사

이후 프로그램이 실행되면 인쇄된 결과는 다음과 같습니다.
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
로그인 후 복사

문제가 없는 것 같습니다. __init__ 메서드에 일부 IO 작업이 있으면 문제가 발생하기 때문입니다.


아래에서는 time.sleep을 통해 시뮬레이션합니다. 위의 __init__ 메서드에 다음 코드를 추가합니다.

def __init__(self):
 import time
 time.sleep(1)
로그인 후 복사

프로그램을 다시 실행한 후 결과는 다음과 같습니다.

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>
로그인 후 복사

문제가 발생합니다! 위와 같은 방식으로 생성된 싱글톤은 멀티스레딩을 지원할 수 없습니다.


해결책: 잠그세요! 잠금 해제된 부분은 동시에 실행되고, 잠긴 부분은 순차적으로 실행되므로 속도는 떨어지지만 데이터 보안은 보장됩니다.

import time
import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
 time.sleep(1)
 @classmethod
 def instance(cls, *args, **kwargs):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
로그인 후 복사

인쇄된 결과는 다음과 같습니다.

<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
로그인 후 복사

거의 다 끝났지만 여전히 작은 문제가 있습니다. 즉, 프로그램이 실행될 때 time.sleep(20)이 실행된 후 아래 개체가 인스턴스화될 때입니다. , 이는 이미 싱글톤 패턴입니다.


그래도 잠금을 추가했는데 좋지 않네요. 몇 가지 최적화를 하고 인스턴스 방식을 다음과 같이 변경해 보겠습니다.

@classmethod
def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
로그인 후 복사

이렇게 하면 멀티스레딩을 지원할 수 있는 싱글톤 모드가 완성됩니다. +

import time
import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
 time.sleep(1)
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
로그인 후 복사

이렇게 구현된 싱글톤 모드는 사용에 제한이 있습니다. 나중에 인스턴스화는 obj = Singleton.instance()를 통해 이루어져야 합니다.


obj = Singleton()을 사용하면 이런 식으로 얻는 것은 싱글턴.

__new__ 메소드를 기반으로


위의 예를 통해 싱글톤을 구현할 때 스레드 안전성을 보장하기 위해 내부 잠금을 추가해야 함을 알 수 있습니다.


객체를 인스턴스화할 때 먼저 클래스의 __new__ 메서드를 실행하고(작성하지 않으면 기본적으로 object.__new__가 호출됨) 객체를 인스턴스화한 다음 __init__ 메서드를 실행합니다. 이 객체는 초기화되므로 이를 기반으로 싱글톤 패턴을 구현할 수 있습니다.

import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
 pass
 def __new__(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = object.__new__(cls)
 return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
def task(arg):
 obj = Singleton()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
로그인 후 복사

인쇄된 결과는 다음과 같습니다.
<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
로그인 후 복사

이 싱글톤 모드를 사용하면 나중에 객체를 인스턴스화할 때 객체를 인스턴스화하는 방법은 일반적인 obj = Singleton()과 동일합니다.


메타클래스 메소드 기반으로 구현

관련지식:


클래스는 타입별로 생성되며, 타입의 __init__ 메소드가 자동으로 실행되며, class()는 __call__ 메소드를 실행한다. 유형(클래스의 __new__ 메서드, 클래스의 __init__ 메서드).


객체는 클래스에 의해 생성됩니다. 객체 생성 시 클래스의 __init__ 메서드가 자동으로 실행되고, object()는 클래스의 __call__ 메서드를 실행합니다.

例子:
class Foo:
 def __init__(self):
 pass
 def __call__(self, *args, **kwargs):
 pass
obj = Foo()
# 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
obj()# 执行Foo的 __call__ 方法
로그인 후 복사

메타클래스 사용법:

class SingletonType(type):
 def __init__(self,*args,**kwargs):
 super(SingletonType,self).__init__(*args,**kwargs)
 def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类
 print('cls',cls)
 obj = cls.__new__(cls,*args, **kwargs)
 cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
 return obj
class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType
 def __init__(self,name):
 self.name = name
 def __new__(cls, *args, **kwargs):
 return object.__new__(cls)
obj = Foo('xx')
로그인 후 복사
    싱글톤 모드 구현:
  1. import threading
    class SingletonType(type):
     _instance_lock = threading.Lock()
     def __call__(cls, *args, **kwargs):
     if not hasattr(cls, "_instance"):
     with SingletonType._instance_lock:
     if not hasattr(cls, "_instance"):
     cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
     return cls._instance
    class Foo(metaclass=SingletonType):
     def __init__(self,name):
     self.name = name
    obj1 = Foo('name')
    obj2 = Foo('name')
    print(obj1,obj2)
    로그인 후 복사

    위 내용은 Python에서 싱글톤 모드를 구현하는 5가지 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:51cto.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿