1. 각 하위 클래스에서 __init__() 구현
이번에는 최상위 클래스에서 __init__() 메소드를 구현하는 것이 아닌, 각 하위 클래스에서 __init__() 메소드를 구현하는 방법에 대해서 알아보겠습니다.
이때, 각 하위 클래스로의 코드 복제를 막기 위해 중복 금지::DRY, Don't Repeat Yourself 원칙을 따르겠습니다.
다음 예제는 하위 클래스에서 초기화를 수행합니다.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | class Suit: def __init__(self, name, symbol): self.name = name self.symbol = symbol class Card: pass class NumberCard(Card): def __init__(self, rank, suit): self.suit = suit self.rank = str(rank) self.hard = self.soft = rank class AceCard(Card): def __init__(self, rank, suit): self.suit = suit self.rank = "A" self.hard, self.soft = 1, 11 class FaceCard(Card): def __init__(self, rank, suit): self.suit = suit self.rank = {11: 'J', 12: 'Q', 13: 'K'}[rank] self.hard = self.soft = 10 def card(rank, suit): if rank == 1: return AceCard('A', suit) elif 2 <= rank < 11: return NumberCard(str(rank), suit) elif 11<= rank < 14: return FaceCard(rank, suit) else: raise Exception("Rank out of range") Club, Diamond, Heart, Spade = Suit('Club', '♣'), Suit('Diamond', '◆'), Suit('Heart', '♥'), Suit('Spade', '♠') deck = [card(rank, suit) for rank in range(1, 14) for suit in (Club, Diamond, Heart, Spade)] print("------------------------------------------------") for i in range(len(deck)): print("OBJECT : {}".format(deck[i])) print("suit : {}".format(deck[i].suit)) print("suit->name : {}".format(deck[i].suit.name)) print("suit->symbol : {}".format(deck[i].suit.symbol)) print("rank : {}".format(deck[i].rank)) print("hard : {}".format(deck[i].hard)) print("soft : {}".format(deck[i].soft)) print("------------------------------------------------") # 공통적인 초기화가 빠져서 불필요한 반복이 발생 # suit의 반복적인 초기화 # 따라서 이를 상위 클래스로 올려야 한다. >>------------------------------------------------ OBJECT : <__main__.AceCard object at 0x104b38400> suit : <__main__.Suit object at 0x104a9b390> suit->name : Club suit->symbol : ♣ rank : A hard : 1 soft : 11 ------------------------------------------------ OBJECT : <__main__.AceCard object at 0x104b38438> suit : <__main__.Suit object at 0x104b38240> suit->name : Diamond suit->symbol : ◆ rank : A hard : 1 soft : 11 ------------------------------------------------ . . . (중략) |
다형성이 분명하게 생겼지만, 공통적인 초기화가 빠져서 불필요한 중복이 발생하게 됩니다.
여기서 불필요한 중복이란 suit의 반복적인 초기화를 의미합니다.
따라서 불필요한 중복을 막기위해서 이를 상위 클래스로 올려야합니다.
이를 구현한 코드는 다음과 같습니다.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | class Suit: def __init__(self, name, symbol): self.name = name self.symbol = symbol class Card: def __init__(self, rank, suit, hard, soft): self.rank = rank self.suit = suit self.hard = hard self.soft = soft class NumberCard(Card): def __init__(self, rank, suit): super().__init__(str(rank), suit, rank, rank) class AceCard(Card): def __init__(self, rank, suit): super().__init__("A", suit, 1, 11) class FaceCard(Card): def __init__(self, rank, suit): super().__init__({11: 'J', 12:'Q', 13:'K'}[rank], suit, 10, 10) def card(rank, suit): if rank == 1: return AceCard(rank, suit) elif 2 <= rank < 11: return NumberCard(rank, suit) elif 11<= rank < 14: return FaceCard(rank, suit) else: raise Exception("Rank out of range") Club, Diamond, Heart, Spade = Suit('Club', '♣'), Suit('Diamond', '◆'), Suit('Heart', '♥'), Suit('Spade', '♠') deck = [card(rank, suit) for rank in range(1, 14) for suit in (Club, Diamond, Heart, Spade)] print("------------------------------------------------") for i in range(len(deck)): print("OBJECT : {}".format(deck[i])) print("suit : {}".format(deck[i].suit)) print("suit->name : {}".format(deck[i].suit.name)) print("suit->symbol : {}".format(deck[i].suit.symbol)) print("rank : {}".format(deck[i].rank)) print("hard : {}".format(deck[i].hard)) print("soft : {}".format(deck[i].soft)) print("------------------------------------------------") # 공통적인 초기화가 빠져서 불필요한 반복이 발생한 것을 제거 # 이전에 비해서 팩토리 함수가 간소화 # 하지만, __init__() method 구현의 공수에 비교했을 때, factroy 함수의 간소화의 효율이 떨어진다. # __init__() method는 직접적인 방법을 사용하고 # 복잡한 부분은 팩토리 함수에 넣는게 더 효율적이다. |
해당 코드는 하위 클래스와 상위 클래스 모두에서 __init__()을 정의합니다. 다음 코드 조각에서 알 수 있듯이
이 방법은 이전에 만든 팩토리 함수를 간소화시키는 이점이 있습니다.
하지만 이런 형태의 변경은 팩토리 함수의 성능 향상이 상대적으로 크지 않은데 비해, __init__() 메소드는 훨씬 복잡해집니다.
2. 단순 복합 개체
복합 객체는 컨테이너::Container라고 부릅니다.
카드 덱을 표현하는 단순 객체를 살펴보겠습니다. 해당 객체는 기본적인 컬렉션입니다.
너무 기본적이라 간단한 list를 덱으로 사용할 수 있습니다.
1 2 3 | deck = [card.rank(r+1).suit(s) for r in range(13) for s in (Club, Diamond, Heart, Spade)] random.shuffle(deck) hand = [deck.pop(), deck.pop()] | cs |
일반적으로 객체의 컬렉션을 디자인 하는 일반적인 디자인 전략은 다음과 같이 3가지가 있습니다.
- 래핑(Wrapping)
- 확장(Extend)
- 개발(Invent)
2-1. 컬렉션 클래스 랩핑
다음은 내부 컬렉션을 포함하는 래퍼 디자인 입니다.
1 2 3 4 5 6 7 8 9 | class Deck: def __init__(self): Club, Diamond, Heart, Spade = Suit('Club', '♣'), Suit('Diamond', '◆'), Suit('Heart', '♥'), Suit('Spade', '♠') card = CardFactory() self._cards = [card.rank(r+1).suit(s) for r in range(13) for s in (Club, Diamond, Heart, Spade)] random.shuffle(self._cards) def pop(self): return self._cards.pop() | cs |
내부 컬렉션을 list 객체로 만드는 Deck을 정의했습니다.
Deck의 pop() 메소드를 래핑된 list 객체에 위임합니다.
이제 다음과 같은 코드를 이용하여 Hand 인스턴스를 생성할 수 있습니다.
1 2 | deck = Deck() hand = [deck.pop(), deck.pop()] | cs |
전체 코드는 다음과 같습니다.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | import random class Suit: def __init__(self, name, symbol): self.name = name self.symbol = symbol class Card: def __init__(self, rank, suit): self.suit = suit self.rank = rank self.hard, self.soft = self._points() class NumberCard(Card): def _points(self): return int(self.rank), int(self.rank) class AceCard(Card): def _points(self): return 1, 11 class FaceCard(Card): def _points(self): return 10, 10 class CardFactory: def rank(self, rank): self.class_, self.rank_str={ 1:(AceCard, 'A'), 11:(FaceCard, 'J'), 12:(FaceCard, 'Q'), 13:(FaceCard, 'K'), }.get(rank, (NumberCard, str(rank))) return self def suit(self, suit): return self.class_(self.rank_str, suit) class Deck: def __init__(self): Club, Diamond, Heart, Spade = Suit('Club', '♣'), Suit('Diamond', '◆'), Suit('Heart', '♥'), Suit('Spade', '♠') card = CardFactory() self._cards = [card.rank(r+1).suit(s) for r in range(13) for s in (Club, Diamond, Heart, Spade)] random.shuffle(self._cards) def pop(self): return self._cards.pop() deck = Deck() hand = [deck.pop(), deck.pop()] print("HAND : {}\n".format(hand)) print("------------------------------------------------") for i in range(len(hand)): print("OBJECT : {}".format(hand[i])) print("suit : {}".format(hand[i].suit)) print("suit->name : {}".format(hand[i].suit.name)) print("suit->symbol : {}".format(hand[i].suit.symbol)) print("rank : {}".format(hand[i].rank)) print("hard : {}".format(hand[i].hard)) print("soft : {}".format(hand[i].soft)) print("------------------------------------------------") | cs |
2-1. 컬렉션 클래스 확장
래핑 외에 내장 클래스를 확장하는 방법도 있습니다.
이렇게 하면, pop() 메소드를 다시 구현하지 않고 단순히 상속받을 수 있습니다.
1 2 3 4 5 6 | class Deck(list): def __init__(self): Club, Diamond, Heart, Spade = Suit('Club', '♣'), Suit('Diamond', '◆'), Suit('Heart', '♥'), Suit('Spade', '♠') card = CardFactory() super().__init__(card.rank(r+1).suit(s) for r in range(13) for s in (Club, Diamond, Heart, Spade)) random.shuffle(self) | cs |
다음 코드를 살펴보면, list를 확장한 Deck 클래스를 정의한 것을 확인할 수 있습니다.
'IT > Python' 카테고리의 다른 글
[Python GUI Programming :: Tkinter] Hello, Again (0) | 2017.10.12 |
---|---|
[Python GUI Programming :: Tkinter] Hello, Tkinter (0) | 2017.10.12 |
[Python GUI Programming :: Tkinter] Tkinter란? (0) | 2017.10.12 |
[객체지향 파이썬 프로그래밍] __init__() method [1] (0) | 2017.07.23 |
[Python3] [Data Structures] list (1) | 2016.12.20 |