파이썬 프로그래밍에서 디자인 패턴(Design Pattern)은 객체지향을 설계하는 과정에서 발생하는 문제를 해결하기 위해 사용되는 패턴이다.
데코레이터 패턴
데코레이터(decorator) 패턴은 @를 사용해 함수나 메서드를 정의하는 방법이다.
def decorator(func):
def wrapper(*args, **kwargs):
print("start")
func(*args, **kwargs) # test(*args, **kwargs)
print("end")
return wrapper
@decorator
def test():
print("Test Decorator")
test()
>>> start
>>> Test Decorator
>>> end
위 예시의 경우, test라는 함수를 decorator 함수의 인자로 받아 사용하고 있다. 데커레이터를 사용하면 wrapper 함수를 계속해서 재사용할 수 있게 된다. 위 코드를 풀어쓰면 다음과 같다.
def decorator():
def wrapper(*args, **kwargs):
print("start")
print("Test Decorator") # test()
print("end")
return wrapper
test = decorator()
test()
@classmethod, @staticmethod
@classmethod는 cls라는 파라미터를 통해 클래스에 접근한다. 일반적인 인스턴스 메서드는 self를 통해 클래스 인스턴스를 받아오는 것과 다르게, 클래스 메서드는 cls를 통해 클래스 자체를 가져온다.
@staticmethod는 정적메서드를 구현할 때 사용한다. 첫 번째 매개변수로 self를 받아오지 않기 때문에 클래스 속성에 접근할 수 없다. 비슷한 맥락의 메서드를 클래스로 묶어두지만, 독립적으로 관리하고 싶을 때 사용할 수 있다.
class Main(object):
@classmethod
def class_method(cls):
print("Class Method")
@staticmethod
def static_method():
print("Static Method")
Main.class_method()
Main.static_method()
>>> Class Method
>>> Static Method
클래스 메서드와 인스턴스 메서드의 차이는 아래 예제를 통해 확인할 수 있다.
class Main(object):
_var = "A"
def __init__(self):
self._var = "B"
def instance_method(self):
print(self._var)
@classmethod
def class_method(cls):
print(cls._var)
main = Main()
main.instance_method()
>>> B
Main.class_method()
>>> A
cls는 초기화되지 않은 <class '__main__.Main'>를 뜻하기 때문에 B가 아닌 A를 가져온다. (cls는 B에 접근할 수 없다.)
옵저버 패턴
옵저버(observer) 패턴은 하나의 객체가 일대다 관계를 가질 때, 종속된 하위 객체에 일괄적으로 내용을 전달하는 방식을 뜻한다.
class Subject(object):
def __init__(self):
self._observers = set()
def add_observer(self, new):
self._observers.add(new)
def update(self):
for observer in self._observers:
observer.plus_two()
class Observer(object):
def __init__(self, var):
self._var = var
def plus_two(self):
self._var += 2
three = Observer(3)
seven = Observer(7)
subject = Subject()
subject.add_observer(three)
subject.add_observer(seven)
subject.update()
print(three._var)
>>> 5
print(seven._var)
>>> 9
Observer의 add_two 메서드를 Subject의 update 메서드를 통해 한 번에 실행시킬 수 있다.
@property 활용
아래 코드에서 get_var 메서드를 사용해 정보를 가져오고, set_var 메서드를 이용해 정보를 수정한다.
class Test(object):
def __init__(self):
self._var = 0
def get_var(self):
# getter
return self._var
def set_var(self, new_var):
# setter
self._var = new_var
test = Test()
test.set_var(5)
var = test.get_var()
print(var)
>>> 5
하지만 @property를 활용하면 외부에서 get이나 set을 숨기고 사용할 수 있다.
class Test(object):
def __init__(self):
self._var = 0
@property
def var(self):
return self._var
@var.setter
def var(self, new_var):
self._var = new_var
test = Test()
test.var = 3
var = test.var
print(var)
>>> 3
@property를 이용해 값을 가져오고, @변수명. setter를 활용해 정보를 수정한다.
싱글턴 패턴
싱글턴(singleton) 패턴은 전역에서 접근 가능한 하나의 객체만을 허용해 다른 값이 생성되어 충돌하는 것을 막는다. 아래 예시는 _instance를 하나만 생성하여 다른 인스턴스가 생성되는 것을 막는다.
class SingleTon(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
class Var(SingleTon):
def __init__(self, var):
self._var = var
a = Var(5)
print(f"before b; a = {a._var}")
b = Var(10)
print(f"after b; a = {a._var}")
print(f"after b; b = {b._var}")
>>> before b; a = 5
>>> after b; a = 10
>>> after b; b = 10
a와 b는 별도로 정의한 인스턴스임에도 불구하고, a._var가 5로 생성되었지만 b가 생성되면서 a, b 모두 10으로 변경되었다.
print(a is b)
print(a._instance)
print(b._instance)
>>> True
>>> <__main__.SingleTon object at 0x00000190834611C8>
>>> <__main__.SingleTon object at 0x00000190834611C8>
a is b가 True라는 것은 a. b 모두 같은 값을 참조하고 있다는 뜻이다. 실제로 확인해보면 _instance는 같은 메모리 주소를 사용하고 있다.