Python의 추상 클래스와 MutableMapping을 활용한 데이터 구조 설계
Python은 객체 지향 프로그래밍(OOP) 언어로서, 추상 클래스를 통해 명확한 인터페이스를 정의하고 이를 기반으로 다양한 데이터 구조를 설계할 수 있는 기능을 제공합니다. 특히, Python의 collections.abc 모듈에서 제공하는 MutableMapping 추상 클래스를 활용하면, 딕셔너리와 같은 유연한 데이터 구조를 커스터마이즈하고 확장할 수 있습니다.
이 글에서는 Python의 추상 클래스 개념과 MutableMapping을 활용한 데이터 구조 설계 방법을 설명하고, 실질적인 예제를 통해 그 활용 방법을 소개합니다.
추상 클래스 (Abstract Class)
추상 클래스는 하나 이상의 추상 메서드를 포함하는 클래스입니다. 추상 메서드는 선언만 되어 있고, 실제 구현은 제공하지 않습니다. 추상 클래스는 객체를 직접 생성할 수 없으며, 반드시 상속받아 추상 메서드를 구현해야 합니다. 이를 통해 일관된 인터페이스를 강제할 수 있습니다.
추상 클래스의 정의
from abc import ABC, abstractmethod
class AbstractDataStructure(ABC):
@abstractmethod
def add(self, item):
pass
@abstractmethod
def remove(self, item):
pass
@abstractmethod
def find(self, item):
pass
위 예제에서 AbstractDataStructure는 추상 클래스이며, add, remove, find 메서드는 추상 메서드로 정의되어 있습니다. 이 클래스는 상속받아 구체적인 구현을 제공해야 합니다.
MutableMapping 추상 클래스
collections.abc 모듈의 MutableMapping 추상 클래스는 딕셔너리와 같은 매핑 타입을 정의하는 데 사용됩니다. MutableMapping은 Mapping을 상속받아 변경 가능한 매핑 타입의 인터페이스를 정의합니다.
MutableMapping을 활용한 사용자 정의 클래스
MutableMapping을 상속받아 커스터마이즈된 딕셔너리 클래스를 구현할 수 있습니다. 이를 통해 기본 딕셔너리 기능을 확장하거나 새로운 기능을 추가할 수 있습니다.
from collections.abc import MutableMapping
class CustomDict(MutableMapping):
def __init__(self):
self._store = {}
def __getitem__(self, key):
return self._store[key]
def __setitem__(self, key, value):
self._store[key] = value
def __delitem__(self, key):
del self._store[key]
def __iter__(self):
return iter(self._store)
def __len__(self):
return len(self._store)
# CustomDict 사용 예제
custom_dict = CustomDict()
custom_dict['a'] = 1
custom_dict['b'] = 2
print(custom_dict['a']) # 출력: 1
print(custom_dict) # 출력: {'a': 1, 'b': 2}
del custom_dict['a']
print(custom_dict) # 출력: {'b': 2}
print(len(custom_dict)) # 출력: 1
CustomDict에 예제 메서드 추가 및 활용
CustomDict 클래스에 예제 메서드를 추가하여 실제로 어떻게 활용할 수 있는지 살펴보겠습니다. 이 예제에서는 increment라는 메서드를 추가하여, 특정 키의 값을 증가시키는 기능을 구현해보겠습니다.
CustomDict 클래스 정의 및 예제 메서드 추가
from collections.abc import MutableMapping
class CustomDict(MutableMapping):
def __init__(self):
self._store = {}
def __getitem__(self, key):
return self._store[key]
def __setitem__(self, key, value):
self._store[key] = value
def __delitem__(self, key):
del self._store[key]
def __iter__(self):
return iter(self._store)
def __len__(self):
return len(self._store)
# 예제 메서드 추가: 특정 키의 값을 증가시키는 메서드
def increment(self, key, amount=1):
if key in self._store:
self._store[key] += amount
else:
self._store[key] = amount
# CustomDict 사용 예제
custom_dict = CustomDict()
custom_dict['a'] = 1
custom_dict.increment('a')
custom_dict.increment('b', 5)
print(custom_dict['a']) # 출력: 2
print(custom_dict['b']) # 출력: 5
print(custom_dict) # 출력: {'a': 2, 'b': 5}
위 예제에서 CustomDict 클래스에 increment 메서드를 추가하여, 특정 키의 값을 증가시키는 기능을 구현했습니다.
Dictionary에서 비슷하게 구현
기본 dict를 사용하여 비슷한 기능을 구현할 수도 있습니다. 이를 위해 별도의 함수를 정의해보겠습니다.
# 기본 dict를 사용하여 increment 기능을 제공하는 함수
def increment(d, key, amount=1):
if key in d:
d[key] += amount
else:
d[key] = amount
# dict 사용 예제
basic_dict = {'a': 1}
increment(basic_dict, 'a')
increment(basic_dict, 'b', 5)
print(basic_dict['a']) # 출력: 2
print(basic_dict['b']) # 출력: 5
print(basic_dict) # 출력: {'a': 2, 'b': 5}
CustomDict와 기본 dict를 사용한 구현의 장단점
CustomDict의 장점
- 메서드 추가 및 확장 용이:
- 클래스 메서드로 기능을 구현하면, 객체 지향적인 접근이 가능하고, 상태를 쉽게 관리할 수 있습니다.
- 메서드를 통해 기능을 캡슐화하여 코드의 재사용성과 유지보수성을 높일 수 있습니다.
- 인터페이스 일관성:
- MutableMapping을 상속받아 딕셔너리와 동일한 인터페이스를 제공하므로, 기존의 딕셔너리 사용 패턴과 호환됩니다.
- 추가 기능 구현의 용이성:
- CustomDict 클래스를 확장하여 새로운 기능을 쉽게 추가할 수 있습니다.
CustomDict의 단점
- 복잡성 증가:
- 기본 dict를 사용하는 것보다 클래스를 정의하고 관리하는 데 더 많은 코드와 복잡성이 필요합니다.
- 성능 저하 가능성:
- 추가된 추상화 계층으로 인해 성능이 약간 저하될 수 있습니다.
기본 dict의 장점
- 단순성:
- 별도의 클래스를 정의하지 않고, 단순한 함수로 기능을 구현할 수 있어 코드가 간결합니다.
- 기본 dict는 Python의 내장 자료형이므로 추가적인 학습이나 설정 없이 바로 사용할 수 있습니다.
- 성능:
- Python의 기본 dict는 C로 구현되어 있어 매우 빠릅니다.
기본 dict의 단점
- 재사용성 및 유지보수성 저하:
- 기능을 함수로 구현하면, 상태와 기능이 분리되어 있어 재사용성과 유지보수성이 낮아질 수 있습니다.
- 여러 함수로 기능을 확장하는 경우, 코드의 일관성을 유지하기 어렵습니다.
- 객체 지향 프로그래밍의 한계:
- 객체 지향 프로그래밍의 이점을 활용하지 못하므로, 복잡한 상태 관리나 기능 확장이 어렵습니다.
결론
- CustomDict: 객체 지향적인 접근으로, 상태와 기능을 캡슐화하여 코드의 재사용성과 유지보수성을 높일 수 있습니다. 복잡한 기능을 확장하는 데 유리하지만, 기본 dict보다 구현과 관리가 더 복잡합니다.
- 기본 dict: 간단하고 성능이 우수하지만, 복잡한 기능을 확장하거나 유지보수할 때 어려움이 있을 수 있습니다.
두 접근 방식 모두 장단점이 있으므로, 특정 상황과 요구 사항에 따라 적절한 방법을 선택하는 것이 중요합니다.
'Computer Science > python' 카테고리의 다른 글
동적 속성과 프로퍼티 (0) | 2024.08.16 |
---|---|
연산자 오버로딩 (0) | 2024.07.09 |
프로토콜과 'abc' 모듈 (0) | 2024.06.11 |
Python 데코레이터 (0) | 2024.05.21 |
seaborn clustermap color label (0) | 2022.05.24 |