일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 우분투 파이썬
- unit test
- prisma
- 안드로이드 디자인패턴
- Dependency Injection
- 안드로이드 테스트
- 유니티
- 자바
- PYTHON
- Data structure
- graphQL
- mvvm
- Design Pattern
- LinkedList
- 웹크롤링
- Nexus GraphQL
- Android test
- flutter
- 안드로이드
- MVVM pattern
- 파이썬 크롤링
- java
- 자바기초
- Apollo Server
- Kotlin
- ubuntu python
- dagger-hilt
- 안드로이드 mvp
- Apollo GraphQL
- Android
- Today
- Total
Hun's Blog
[Python] 파이썬으로 Singleton 패턴 구현하기 본문
디자인 패턴에 관련된 자세한 내용은 아래 링크에 정리해 두었다.
https://jroomstudio.tistory.com/20?category=386216
Singleton
하나의 클래스에 대해 어플리케이션이 시작될 때 최초 한번만 메모리를 할당하고 그 메모리에 인스턴스를 생성한다. 즉, 인스턴스를 단 하나만 생성한다.
장점
- 고정된 메모리영역을 얻어 하나의 인스턴스만 생성하기 때문에 메모리 낭비를 방지한다.
- 인스턴스가 전역적으로 사용될 수 있기에 다른 클래스의 인스턴스들이 데이터를 공유하고 변경할 수 있다.
단점
- 싱글톤 인스턴스에게 많은일을 위임하거나 데이터를 공유시킬 경우 다른 클래스의 인스턴스간에 결합도가 높아져 개방폐쇄원칙에 위배된다.
- 멀티스레드 환경에서 데이터 동기화 문제가 발생할 수 있음 (Synchronized 키워드 활용)
- 너무 많이 사용하지 않도록 한다.
*싱글톤은 하나의 클래스에 단 하나의 인스턴스를 허용하는 패턴이다.
1. __new__ 생성자를 이용하여 Sinlgeton 구현
Singleton.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Singleton(object):
def __new__(cls):
if not hasattr(cls,'instance'):
print('create')
cls.instance = super(Singleton, cls).__new__(cls)
else:
print('recycle')
return cls.instance
print('1번째 생성')
s1 = Singleton() # create
print('2번째 생성')
s2 = Singleton() # recycle
print('s1 == s2')
print(s1==s2) # true
|
1) class Singleton(object):
-> 자바와 마찬가지로 클래스가 존재한다.
-> 또한 동일하게 객체로 만들어 사용하여 객체지향 프로그래밍을 구현할 수 있다.
-> 클래스명 옆에 (object)는 python3 에서 자동으로 상속받기 때문에 없어도 오류가 발생하지 않는다.
2) def __new__(cls):
* __new__
-> 인스턴스를 *새로 만들 때 처음으로 실행되는 메소드이다.
-> 인스턴스가 생성될때 어떻게 할 것인지 재정의 하여 sinlgeton을 구현할수있다.
-> 입력값인 (cls) 는 변경할 수 없다.
-> cls 는 해당 클래스가 생성되면서 새로 생긴 class 즉 객체이다.
3) if not hasattr(cls,'instance'):
-> if not 은 fase 일때 실행하겠다는 의미이다.
-> hasattr(object,name) 메소드는 object에 nam에 해당하는 attribute 가 있으면 true 없으면 false 를 반환한다.
-> print('create') 으로 else 는 print('recycle') 로 각각 생성과 재사용을 구분한다.
4) cls.instance = super(Singleton, cls).__new__(cls)
-> cls의 instance 변수에 새로운 인스턴스를 생성하여 담는다.
-> 없을경우 새로운 객체를 생성하여 instance에 담아 반환한다.
-> 클래스 내부에 instance가 존재하면 생성된 instance를 반환한다.
5) return cls.instance
-> return 하지않으면 객체를 반환하지 않으므로 반드시 return 해주어야 한다.
-> hasattr() 메소드로 true/false 를 판별하기 때문에 생성 후에는 기존의 인스턴스만 반환하게된다.
2. Lazy instantiation
Lazy instantiation (게으른 초기화)
- 객체를 필요할 때 만든다.
- 사용할 수 있는 리소스가 제한적인 상황에서 꼭 필요한 시점에 객체를 생성한다.
LazyInstantiation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class LazyInstantiation:
_instance = None
def __init__(self):
if not LazyInstantiation._instance:
print('__init__ method called but nothing is created')
else:
print('instance already created:', self.getInstance())
@classmethod
def getInstance(cls):
if not cls._instance:
cls._instance = LazyInstantiation()
return cls._instance
# __init__ method called but nothing is created
s = LazyInstantiation()
print(s._instance) # None
# __init__ method called but nothing is created
s1 = LazyInstantiation.getInstance()
s2 = LazyInstantiation.getInstance()
print('s와 s1은 서로 같은 인스턴스인가? -> ',s==s1) # True
print('s1 : ',s1._instance) # <__main__.LazyInstantiation object at 0x03A6B340>
print('s2 : ',s2._instance) # <__main__.LazyInstantiation object at 0x03A6B340>
s3 = LazyInstantiation() # instance already created: <__main__.LazyInstantiation object at 0x03A6B340>
print('s3 : ',s3._instance) # <__main__.LazyInstantiation object at 0x03A6B340>
|
1) class LazyInstantiation:
-> 클래스 생성
2) _instance = None
-> _instance 라는 전역변수를 생성하고 값을 None(null) 으로 한다.
3) def __init__(self):
* __init__
-> 자바의 생성자와 비슷한 역할을 한다.
-> 객체의 인스턴스를 생성할 때 자동으로 호출된다.
-> __new__ 메소드는 객체를 생성할 때 호출되는 것이고 __init__ 은 객체가 활성화 될 때 호출된다.
-> 그렇기 때문에 15번 라인과 같이 생성하기 전에 메세지를 출력한 것이다.
-> 첫번째 인자값은 반드시 self 여야 한다.
4) if not LayzInstantiation._instance:
-> 전역변수 _instance 가 None 이면 fasle 이기 때문에 None 일때 실행된다.
-> 값이 들어있을 때는 instance를 출력한다.
5) @classmethod
-> 정적 메소드 구현방법 중 하나
-> 인스턴스를 생성하지 않아도 메서드를 바로 실행할 수 있다.
-> 스크립트의 19,20번 라인을 보면 클래스를 클래스() 형태로 생성하지 않고 getInstance()로 접근한다.
-> 클래스가 생성되지 않았음에도 getInstance() 를 통해 클래스의 instance를 가져온다.
6) def getInstance(cls)
-> cls는 클래스를 의미한다. 여기서 클래스는 메소드가 속한 클래스를 의미한다.
-> if not cls._instance
-> 해당 클래스의 전역변수 _instance 가 None 이라면 LazyInstantiation() 을 통해 인스턴스를 생성하고 담는다.
-> 생성된 혹은 생성되어있는 cls_instance 를 반환한다.
결과
-> __init__ method called but nothing is created 메세지 출력
-> 이미 해당 클래스에서 run이 된 순간부터 __init__ 클래스가 호출된다.
-> 아직 전역변수 _instance에 값이 없으므로 아직 만들어지지 않았다는 메세지가 출력되었다.
-> None , __init__ method called but nothing is created 메세지 출력
-> s = LazyInstantiation()
-> 1번 예제와 같이 클래스 객체를 생성해보았다.
-> 생성 후 s._instance 를 출력했을때 생성이 되지 않아 None 으로 출력되었다.
-> 클래스가 다시 활성화 되었기 때문에 __init__ 이 실행되었고 여전히 _instance 는 None 이기 때문에 if not 이 실행된다.
-> s,s1 을 getInstance() 메소드로 인스턴스를 가져온다.
-> s와 s1은 서로 같은 인스턴스인가? -> True
-> 맨 처음 s1 = LayzInstantiation.getInstance() 를 호출하였다.
-> 클래스 전역변수인 _instance가 None 이므로 LazyInstantiation() 으로 인스턴스를 생성한다.
-> 두번째로 s2 = LayzInstantiation.getInstance() 를 호출하였다.
-> 이전에 인스턴스가 생성되어 _instance에 값이 들어갔으므로 새로 생성하지 않고 그대로 반환한다.
-> s1._instance 와 s2._instance를 출력해보는 같은 값을 가지고 있다
-> 그러므로 s1과 s2 는 서로 같기 때문에 True 를 반환한다.
-> instance already created: <__main__.LazyInstantiation object at 0x03A6B340> 출력
-> s3 = LazyInstantiation() 으로 다시한번생성
-> 클래스가 활성화되면서 __init__ 이 호출되고 이번엔 _instance에 값이 있으므로 else를 실행한다.
-> s3._instance 를 출력해보면 이전에 생성된 instance와 동일한 것을 확인할 수 있다.
// __new__ 로 생성한 것과 간단하게 차이를 나눠보자면
__new__ -> 클래스 객체를 생성할 때 동작하고 이곳에서 인스턴스 생성작업을 한다.
-> 클래스를 생성하는 첫번째 순간에 인스턴스가 반드시 생성되어 반환된다.
__init__ -> 클래스가 활성화 될때 실행되며 단순히 인스턴스의 생성 여부를 반환한다.
-> 내부 클래스인 getInstance 를 통해서 인스턴스를 생성하고 반환한다.
3. Monostate Singleton
- 싱글톤 패턴에 의하면 클래스의 객체는 하나만 존재해야 한다.
- 일부 개발자는 객체 생성 여부보다 객체의 상태와 행위가 더 중요하다고 한다.
- 모노스테이트는 생성된 여러 객체가 같은 상태를 공유하는 패턴이다.
MonostateSingleton.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class MonostateSingleton:
__shared_state = {'a':'b'}
def __init__(self):
self.__dict__ = self.__shared_state
m1 = MonostateSingleton()
m2 = MonostateSingleton()
print(m1) # <__main__.MonostateSingleton object at 0x02C4B0E8>
print(m2) # <__main__.MonostateSingleton object at 0x02C4B118>
m1.a = 1
m2.b = 2
print(m1.__dict__) # {'a': 1, 'b': 2}
print(m2.__dict__) # {'a': 1, 'b': 2}x03A6B340>
|
1) class MonostateSingleton:
-> 클래스 생성
2) __shared_state = {'a':'b'}
- {'a':'b'} 딕셔너리가 생성되었다.
- 리스트와 비슷하지만 각각의 요소가 key와 value로 구성되어있다.
3) def __init__(self): -> self.__dict__ = self.__shared_state
*__dict__
-> 현재 인스턴스와 클래스의 속성을 딕셔너리로 가지고있다.
-> __dict__ 에 전역변수로 선언한 {'a':'b'} 딕셔너리를 셋팅한다.
-> m1 = MonostateSingleton() , m2 = MonostateSingleton()
-> m1 과 m2 를 출력해보니 서로 다른 인스턴스임을 확인할 수 있다.
-> 객체가 생성될 때 __dict__ 에 전역변수 {'a':'b'} 가 생성된다.
-> __dict__에 셋팅된 딕셔너리는 다음과 같이 호출하여 값을 셋팅할 수 있다.
-> m1.a =1 , m2.b =2
-> 서로 다른 인스턴스인 m1과 m2에 각각 다른 값을 입력한다.
-> m1.__dict__ , m2.__dict 를 출력했을 때 같은 값을 출력한다.
// 인스턴스가 다르더라도 __dict__ 를 활용하면 같은 상태를 공유할 수 있다.
4. Singleton + metaclass
- 메타 클래스는 클래스의 클래스이다.
- 클래스는 메타 클래스의 인스턴스가 된다.
- 메타클래스를 활용하여 이미 정의된 클래스를 통해 새로운 클래스를 생성한다.
MetaclassSingleton.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class MetaclassSingleton(type):
_instance = {}
def __call__(cls,*args,**kwargs):
if cls not in cls._instance:
cls._instance[cls] = super(MetaclassSingleton, cls).__call__(*args,*kwargs)
return cls._instance[cls]
class TestMetaclass(metaclass=MetaclassSingleton):
pass
class SecondMetaclass(metaclass=MetaclassSingleton):
pass
t = TestMetaclass()
t1 = TestMetaclass()
print(t) # <__main__.TestMetaclass object at 0x037FB100>
print(t1) # <__main__.TestMetaclass object at 0x037FB100>
t2 = SecondMetaclass()
t3 = SecondMetaclass()
print(t2) # <__main__.SecondMetaclass object at 0x037FB310>
print(t3) # <__main__.SecondMetaclass object at 0x037FB310>
print(MetaclassSingleton._instance)
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
1) class MetaclassSingleton(type)
-> type 을 상속받은 클래스이다. 커스텀 메타클래스를 만들기 위해 상속받는다.
2) _instance = {}
-> 전역변수인 _instance를 비어있는 딕셔너리로 생성한다.
3) def __call__(cls,*args,*kwargs)
*__call__
-> 클래스의 객체를 호출할수 있게 도와주는 메소드이다.
-> 객체를 메소드 호출 방식으로 가져올수있게 하는 매직 메소드이다.
*args
-> arguments의 줄임말
-> 다른 단어를 써도 된다.
-> 이 지시어는 복수의 인자를 함수로 받고자 할 때 사용된다.
*kwargs
-> keyword argument 줄임말
-> 키워드를 제공한다.
-> 딕셔너리 형태로 {'키워드':'특정값'} 함수 내부로 전달된다.
4) if cls not in cls._instance:
-> 현재 cls가 _instance 딕셔너리에 없다면
-> cls._instance[cls] = super(MetaclassSingleton, cls).__call__(*args,*kwargs)
-> MetaclassSingleton 인스턴스를 현재 클래스로 생성하여 _instance 딕셔너리에 추가한다.
5) TestMetaclass() , SecondMetaclass()
-> metaclass=MetaclassSingleton 으로 지정 한다.
-> t = TestMetaclass() ,t1 = TestMetaclass()
-> t를 TestMetaclass() 로 생성하였다.
-> metaclass 로 지정한 클래스가 생성되면 __call__ 메소드가 호출된다.
-> _instance 딕셔너리를 검사하여 TestMetaclass로 생성한 인스턴스가 있는지 확인한다.
-> 없으므로 새로 생성한다.
-> t1 도 동일하게 생성한다.
-> 이번엔 이미 인스턴스가 존재함으로 _instance 딕셔너리에서 가져온 인스턴스를 반환한다.
-> 출력해보면 t,t1 은 서로 같은 인스턴스임을 확인할 수 있다.
-> t2 = SecondMetaclass(), t3 = SecondMetaclass()
-> t 와 동일한 방법으로 t2를 생성하되 이번엔 SecondMetaclass() 로 생성하였다.
-> _instance 에는 SecondMetaclass() 로 생성한 인스턴스가 없기때문에 새로 생성하여 추가한다.
-> t3 는 이미 인스턴스가 존재함으로 _instance 딕셔너리에서 가져온 인스턴스를 반환한다.
-> 출력해보면 t2,t3 는 서로 같은 인스턴스임을 확인할 수 있다.
-> MetaclassSingleton._instance 를 출력해보면 TestMetaclass() 로 생성한 인스턴스와 SecondMetaclass() 로 생성한 인스턴스가 각각 딕셔너리 형태로 추가된 것을 확인할 수 있다.
참고
https://medium.com/@chs99051868/python-design-pattern-singleton-963f4a796d7f
'Language > Python' 카테고리의 다른 글
[Python] 웹 크롤링 10 - 네이버 뉴스 댓글 수집하기 (0) | 2020.04.06 |
---|---|
[Python] 웹 크롤링 9 - 네이버 이미지 수집하기 (0) | 2020.04.06 |
[Python] 웹 크롤링 8 - 트위치 클립 다운로드 (0) | 2020.04.06 |
[Python] 웹 크롤링 7 - 색감테스트 봇 구현 (0) | 2020.04.06 |
[Python] 웹 크롤링 6 - 1 to 50 봇 (0) | 2020.04.06 |