믹스인_클래스

매일 공부(ML)·2022년 7월 4일
0

이어드림

목록 보기
91/146

기능을 합성할 때는 믹스인 클래스 사용

파이썬은 다중 상속(편의와 캡슐화)을 처리할 수는 있지만 골치 아픈 경우를 피하기 위해선 mix-in을 사용할지 고려하라

믹스인은 자식 클래스가 사용할 메서드 몇 개만 정의하는 클래스

믹스인 클래스는 자체 애트리뷰트 정의가 없기에 믹스인 클래스의 init메서드를 호출할 필요도 없다


믹스인 장점

  • 타입의 상관없이 객체의 현재 상태를 쉽게 들여다본다.

  • 동적인 상태 접근이 가능하기에 제너릭하다

  • 믹스인을 합성하거나 계층화를 해서 반복적인 코드를 최소화하고 재사용성을 최대화


Code

"""제너릭하게 여러 클래스 활용
"""

class ToDictMixin:
    def to_dict(self):
        return self._traverse_dict(self.__dict__)

        """
        _traverse_dict메서드는 hasattr을 통해서 동적인 애트리튜브 접근
        isinstance를 사용한 타입 검사, __dict__통한 인스턴스 딕셔너리 접근 활용
        """

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

    def _traverse(self, key, value):
        if isinstance(value, ToDictMixin):
            return value.to_dict()
        elif isinstance(value, dict):
            return self._traverse_dict(value)
        elif 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
"""믹스인을 사용해서 이진 트리를 딕셔너리로 표현
"""

class BinaryTree(ToDictMixin):
    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())
"""BinaryTreeWithParent._traverse메서드를 오버라이드하여 문제가 되는 값만 처리하여
무한루프를 돌지 못하게 한다.
"""

class BinaryTreeWithParent(BinaryTree):
    def __init__(self, value, left=None,
                 right=None, parent=None):
        super().__init__(value, left=left, right=right)
        self.parent =parent

    """
    부모를 가리키는 참조에 대해서 부모의 숫자값을 삽입
    그렇지 않은 경우, super 내장함수를 통해서 디폴트 믹스인 구현 호출
    """

    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())

"""
BinaryTreeWithParent._traverse를 오버라이드하니 
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()) #무한 루프 없다
#믹스인 서로 합성
import json

class JsonMixin:
    @classmethod
    def from_json(cls,data):
        kwargs = json.loads(data)
        return cls(**kwargs)

    def  to_json(self):
        return json.dumps(self.to_dict())
#믹스인이 있다면 JSON과 직렬화 혹은 역직렬화
#클래스 계층 구조를 쉽게, 번잡스러운 준비 코드 없이 만든다.

class DatacenterRack(ToDictMixin, JsonMixin):
    def __init__(self, switch=None, machines =None):
        self.switch = Switch(**switch)
        self.machines = [
            Machine(**kwargs) for kwargs in machines
        ]

class Switch(ToDictMixin, JsonMixin):
    def __init__(self, ports=None, speed=None):
        self.ports = ports
        self.speed = speed

class Machine(ToDictMixin, JsonMixin):
    def __init__(self, cores= None, ram= None, disk=None):
        self.cores = cores
        self.ram = ram
        self.disk = disk
#검사해보기
serialized = """{
    "switch": {"ports":5, "speed": 1e9},
    "machines": [
        {"cores":8, "ram": 32e9, "disk":5e12},
        {"cores":4, "ram": 16e9, "disk":1e12},
        {"cores":2, "ram": 4e9, "disk":500e9}
    ]
}"""
deserialized = DatacenterRack.from_json(serialized)
roundtrip = deserialized.to_json()
assert json.loads(serialized) ==json.loads(roundtrip)
profile
성장을 도울 아카이빙 블로그

0개의 댓글