[python] Mix-in in OOP

건너별·2022년 6월 24일
0

python

목록 보기
11/12

| python 에서는 다중상속이라는 편리한 기능이 있지만 그 때문에 골치아픈 경우가 발생합니다. 그를 피할 수 있는 방법이 MIX-IN 입니다.😊

class 다중상속 시 문제

  • A,B,C 는 수정 불가능한 패키지
  • D,E 는 각각 B,C 로부터 상속받음

-> 이 경우, code duplicate가 일어남

해결 방안 : mixin

  • 공통된 함수를 M이라는 mixin class 안에 정의함

  • 자식 클래스가 사용할 METHOD 몇개만 정의하는 클래스(attribute X)

  • attribute 없으므로 init method(constructor)도 필요없음

  • 믹스인 클래스를 작성해두면 여러 클래스에 적용 가능함!(Generic)

case 1

class A:
  total = 345
class B(A):
  pass
class C(A):
  pass
# locked packages so far.

class M:
  def print_total(self):
    print(self.total)

class D(B,M):
  pass

class E(C,M):
  pass

e = E()
e.print_total()

  • Mixin class M 으로부터 상속받아 self.total 을 duplicate 없이 호출할 수 있게 됨

case 2 : 메모리 내 파이썬 객체를 딕셔너리로 변환(직렬화에 사용)

1. ToDictMixin 클래스 정의

class ToDictMixin:
  def to_dict(self):
    return self._traverse_dict(self.__dict__)
    
  def _traverse_dict(self, instance_dict):
    output = {}
    for key, value in instance_dict.items():
      output[key] = self._traverse(key, value)
    return output

#  [Bool] = isinstance(확인하고자 하는 데이터 값, 확인하고자 하는 데이터 타입)

  def _traverse(self, key, value):
    if isinstance(value, ToDictMixin):
      return value.to_dict()
    if isinstance(value, dict):
      return self._traverse_dict(value)
    if isinstance(value, list):
      return [self._traverse(key, i) for i in value]
    elif hasattr(value, '__dict__'):
      return self._traverse_dict(value.__dict__)
    else:
      return value

2. 이를 이용하여 이진트리-> 딕셔너리 변환!

  • ToDictMixin 상속하여 to_dict()
class BinaryTree(ToDictMixin): # mixin class 상속
  def __init__(self, value, left=None, right=None):
    self.value = value
    self.left = left
    self.right = right

tree = BinaryTree(10,
                  left = BinaryTree(7, right=BinaryTree(9)),
                  right = BinaryTree(13, left=BinaryTree(11))
                  )
print(tree.to_dict())

>>>{'value': 10, 'left': {'value': 7, 'left': None, 'right': {'value': 9, 'left': None, 'right': None}}, 'right': {'value': 13, 'left': {'value': 11, 'left': None, 'right': None}, 'right': None}}

믹스인 장점 1 : override로 메소드 변경 가능

다음은 parent attribute가 추가된 클래스 BinaryTreeWithParent

class BinaryTreeWithParent(BinaryTree):
  def __init__(self, value, left=None, right=None, parent=None):
    super().__init__(value, left=left, right=right) #BinaryTree로부터 값 상속
    self.parent = parent

  ## overrriding 으로 순환 참조(무한루프)를 막음 -> to_dict() 사용가능
  def _traverse(self,key,value):
    if isinstance(value, BinaryTreeWithParent) and key=='parent':
      return value.value
    else:
      return super()._traverse(key,value)
    
root = BinaryTreeWithParent(10)
root.left = BinaryTreeWithParent(7, parent=root)
root.left.right = BinaryTreeWithParent(9, parent=root.left)
print(root.to_dict())

>>> {'value': 10, 'left': {'value': 7, 'left': None, 'right': {'value': 9, 'left': None, 'right': None, 'parent': 7}, 'parent': 10}, 'right': None, 'parent': None}
  • 이에 따라 BinaryTreeWithParent를 애트리뷰트로 저장하는 모든 클래스도 자동으로 ToDictMixin을 문제없이 사용하게 됨:
class NamedSubTree(ToDictMixin):
  def __init__(self, name, tree_with_parent):
    self.name = name
    self.tree_with_parent = tree_with_parent

my_tree = NamedSubTree('foobar', root.left.right)
print(my_tree.to_dict())

>>> {'name': 'foobar', 'tree_with_parent': {'value': 9, 'left': None, 'right': None, 'parent': 7}}

믹스인 장점 2 : 합성가능


참고 개념

  • Generic function
  • 다양한 자료형에도 적용할 수 있는 함수를 의미
    def add(a,b): 
        return a + b 
    integer, list, str 등 다양한 자료형에 적용가능!
  • 직렬화 (Serialization)
    객체(Object)일련의 byte로 변환하는 것!
import pickle
 
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height
 
 
rect = Rectangle(10, 20)
 
# 사각형 rect 객체를 직렬화 (Serialization)
with open('rect.data', 'wb') as f:
    pickle.dump(rect, f)
 
 
# 역직렬화 (Deserialization)
with open('rect.data', 'rb') as f:
    r = pickle.load(f)
 
print("%d x %d" % (r.width, r.height))

Reference

profile
romantic ai developer

0개의 댓글