예제. [도서관 도메인] Book 클래스의 클래스 수준의 속성 정의 및 다형성 

도서관 도메인에서 Book클래스의 클래스 속성을 정의하는 예이다. Book 클래스는 BookLoan 클래스와는 다르게  상속구조를 사용한다. 즉, Book 클래스에서 PaperBook과 EBook 클래스가 상속된다. 각각의 클래스는 각각의 특징에 맞게 속성들을 정의하고, 특별히 Book 클래스에서는 클래스 수준의 book_list 속성이 정의되어, 생성된 Book 클래스와 PaperBook/EBook 클래스의 인스턴스들을 book_list 속성에 저장한다. 

 

예상 코드

class Book:
    _book_list = []

    def __init__(self, book_id, title, author, isbn):
        self.id = book_id
        self.title = title
        self.author = author
        self.ISBN = isbn

        Book._book_list.append(self)

    @classmethod
    def display_list(cls):
        print("=" * 110)
        print("전체 도서 목록".center(85))
        print("=" * 110)
        print(f"{'구분':6} {'ID':<4} {'제목':20} {'저자':10} {'ISBN':15} {'추가정보'}")
        print("-" * 100)

        for book in Book._book_list:
            # Book 타입별로 추가정보 생성
            if isinstance(book, PaperBook):
                extra_info = f"서가:{book.shelf_location:<6} 대출가능:{book.available_copies}권"
                book_type = "PBook"
            elif isinstance(book, EBook):
                extra_info = f"형식:{book.file_format:<6} 크기:{book.file_size_mb:<5}MB 동시:{book.max_simultaneous_users}명"
                book_type = "EBook"

            print(f"{book_type:8} {book.id:<4} {book.title:20} {book.author:10} {book.ISBN:15} {extra_info}")

        print("=" * 110)


class PaperBook(Book):
    def __init__(self, book_id, title, author, isbn, shelf_location, available_copies):
        super().__init__(book_id, title, author, isbn)
        self.shelf_location = shelf_location
        self.available_copies = available_copies

    @classmethod
    def display_list(cls):
        print("=" * 85)
        print("일반도서 목록".center(85))
        print("=" * 85)
        print(f"{'ID':<4} {'제목':22} {'저자':10} {'서가':10} {'대출가능권수'}")
        print("-" * 70)

        for book in Book._book_list:
            if isinstance(book, PaperBook):
                print(
                    f"{book.id:<4} "
                    f"{book.title:22} "
                    f"{book.author:10} "
                    f"{book.shelf_location:10} "
                    f"{book.available_copies:<10}"
                )

        print("=" * 85)


class EBook(Book):
    def __init__(self, book_id, title, author, isbn, file_format, file_size_mb, max_simultaneous_users):
        super().__init__(book_id, title, author, isbn)
        self.file_format = file_format
        self.file_size_mb = file_size_mb
        self.max_simultaneous_users = max_simultaneous_users

    @classmethod
    def display_list(cls):
        print()
        print("=" * 85)
        print("전자책 목록".center(85))
        print("=" * 85)
        print(f"{'ID':<4} {'제목':22} {'저자':10} {'형식':8} {'크기(MB)':10} {'동시대출'}")
        print("-" * 75)

        for book in Book._book_list:
            if isinstance(book, EBook):
                print(
                    f"{book.id:<4} "
                    f"{book.title:22} "
                    f"{book.author:10} "
                    f"{book.file_format:8} "
                    f"{book.file_size_mb:<10} "
                    f"{book.max_simultaneous_users:<10}"
                )

        print("=" * 85)


# 객체 생성
book1 = PaperBook(1, "OOP", "홍길동", "978-89-1234", "A-3-2", 3)
book2 = EBook(2, "Python Basic", "김철수", "978-89-5678", "PDF", 12.5, 10)
book3 = PaperBook(3, "Data Structure", "이영희", "978-89-1111", "B-1-4", 2)
book4 = EBook(4, "Algorithm", "박민수", "978-89-2222", "EPUB", 8.3, 5)

Book.display_list()
PaperBook.display_list()
EBook.display_list()
  • 2라인 클래스 수준의 속성 생성:  클래스 수준의 속성으로 _book_list를 리스트 타입으로 정의하였다.
  • 10라인 리스트에 인스턴스 대입: 클래스 인스턴스 생성시, 즉 생성자 __init__()에서 self 인스턴스가 만들어지면 _book_list.append(self)로 __book_list에 해당 인스턴스를 append 한다. 
  • 13라인부터 Book 클래스의 _book_list의 인스턴스를 도서목록으로 출력하는 메소드 
    • 14 ~18라인: 목록의 title을 출력한다. 
    • 20라인 for book in Book._book_list:
      • _book_list의 항목 하나하나를 book 변수에 대입하여, 대입된 인스턴스 type(PaperBook or EBook)에 맞게 추가정보를 생성한다. (다형성 작동)
    • 20~27라인: _book_list에는 PaperBook, EBook 모든 인스턴스가 들어가 있다. 해서 Book의 공통정보인 id, 제목, 저자, ISBN은 공통으로 출력하고, PaperBook과 EBook 고유의 정보는 추가정보로 묶어서 출력한다. 20-27라인 코드가 Book 타입별로 추가정보를 하나의 변수로 생성하는 부분이다. 
    • 29 라인: 실제 도서정보 한건을 출력한다. 
  • 40~58라인: book_list 속성에서 일반도서(PapaerBook) 정보만 출력한다.
    • 49라인:  if isinstance(book, PaperBook):
      • isisnacne() 함수는 첫번째 매개변수가 두번째 매개변수 type인지를 확인해준다. 즉, book 변수에 대입된 인스턴스가 PaperBook 클래스 타입인지를 확인해여 맞으면 True 아니면 False를 리턴한다. 
  • 69~88라인: book_list 속성에서 전자도서(EBook) 정보만 출력한다.
    • 78라인: 49 라인과 동일하게 작동한다. 

 

실습. 키오스크의 메 도메인에서 상속관계 클래스 찾기

 

아래 클래스 다이어그램은 키오스크 도메인에서 MenuItem의 상속구조를 보여준다. 

 

 

위 상속구조에 클래스 수준의 속성인 menu_item_list를 정의하고 도서관 시스템의 예상 클래스 다이어그램과 예상 코드를 참조하여 아래 요구사항에서 상속관계의 클래스를 도출하여 클래스 다이어그램을 그리고, 파이썬 코드로 구현하시오. 

 

제출1. 클래스 다이어그램

제출 2. 파이썬 코드

  • 1. 제출 1을 파이썬으로 구현한 클래스 코드
    • menu_item_list를 정의하고, __init__ ()에서 만들어진 인스턴스를 리스트에 append 해준다. 
  • 2. MenuItem/Coffee/Desser 클래스에서 display_list( ) 메소드를 정의한다.
    • MunuItem 클래스에서는 menu_item_list의 항목들을 모두 목록으로 출력한다.
    • Coffee 클래스의 display_list ()에서는 _menu_item_list에서 Coffee 클래스의 인스턴스만 출력한다.
    • Dessert 클래스의 display_list()에서는 _menu_item_list에서 Dessert 클래스의 인스턴스만 출력한다. 
  • 3. (위) 1번, 2번에서 구현한 클래스에 대해 클래스 인스턴스들을 각각(Coffee, Dessert) 3개씩 생성하고, 전체메뉴, 커피메뉴, 디저트 메뉴를 출력하는 코드를 작성한다.
    • 출력예시
==========================================================================================
                                         전체 메뉴 목록                                         
==========================================================================================
Type     ID   Name                 Price      Available Stock   More Info
------------------------------------------------------------------------------------------
Coffee   1    Americano            3000       10                카페인:120 mg 온도:hot
Coffee   2    Latte                4000       5                 카페인:90  mg 온도:iced
Coffee   3    Mocha                4500       7                 카페인:150 mg 온도:hot
Dessert  4    Cheese Cake          4500       4                 칼로리:350 kcal 보관:냉장
Dessert  5    Macaron              2500       8                 칼로리:180 kcal 보관:실온
Dessert  6    Tiramisu             5000       3                 칼로리:420 kcal 보관:냉장
==========================================================================================

==========================================================================================
                               커피 메뉴 목록                               
==========================================================================================
ID   Name                 Price      Available Stock   caffeine(mg) Temperature
------------------------------------------------------------------------------------------
1    Americano            3000       10                120          hot     
2    Latte                4000       5                 90           iced    
3    Mocha                4500       7                 150          hot     
==========================================================================================

==========================================================================================
                                        디저트 메뉴 목록                                         
==========================================================================================
ID   Name                 Price      Available Stock   Calorie(kcal)  Storage Type
------------------------------------------------------------------------------------------
4    Cheese Cake          4500       4                 350            냉장      
5    Macaron              2500       8                 180            실온      
6    Tiramisu             5000       3                 420            냉장      
==========================================================================================

 

 

클래스 다이어그램 예시

코드 예시

더보기
class MenuItem:
    _menu_item_list = []

    def __init__(self, item_id, name, price, available_stock):
        self._id = item_id
        self._name = name
        self._price = price
        self._available_stock = available_stock

        MenuItem._menu_item_list.append(self)

    def order(self, quantity):
        if quantity <= 0:
            print("주문 수량은 1개 이상이어야 합니다.")
            return

        if self._available_stock >= quantity:
            self._available_stock -= quantity
            print(f"{self._name} {quantity}개 주문 완료")
        else:
            print(f"{self._name} 재고 부족")

    @classmethod
    def display_list(cls):
        print("=" * 90)
        print("전체 메뉴 목록".center(90))
        print("=" * 90)
        print(f"{'Type':8} {'ID':<4} {'Name':20} {'Price':10} {'Available Stock':17} {'More Info'}")
        print("-" * 90)

        for item in MenuItem._menu_item_list:
            if isinstance(item, Coffee):
            #     extra_info = f"카페인:{item.__caffeine_amount:<4}mg 온도:{'hot' if item.__temperature_type == 1 else 'iced'}"
                item_type = "Coffee"
            elif isinstance(item, Dessert):
            #     storage_dict = {1: "실온", 2: "냉장", 3: "냉동"}
            #     extra_info = f"칼로리:{item.__calories_kcal:<4}kcal 보관:{storage_dict.get(item.__storage_type, '알수없음')}"
                item_type = "Dessert"
            else:
            #     extra_info = ""
                item_type = "Menu"

            # print(f"{item_type:8} {item.id:<4} {item.name:20} {item.price:<10} {item.available_stock:<17} {extra_info}")
            print(f"{item_type:8} {item._id:<4} {item._name:20} {item._price:<10} {item._available_stock:<17} ")

        print("=" * 90)


class Coffee(MenuItem):
    def __init__(self, item_id, name, price, available_stock, caffeine_amount, temperature_type):
        super().__init__(item_id, name, price, available_stock)
        self.__caffeine_amount = caffeine_amount
        self.__temperature_type = temperature_type

    @classmethod
    def display_list(cls):
        print()
        print("=" * 90)
        print("커피 메뉴 목록".center(70))
        print("=" * 90)
        print(f"{'ID':<4} {'Name':20} {'Price':10} {'Available Stock':17} {'caffeine(mg)':12} {'Temperature'}")
        print("-" * 90)

        for item in MenuItem._menu_item_list:
            if isinstance(item, Coffee):
                temp_text = "hot" if item.__temperature_type == 1 else "iced"
                print(
                    f"{item._id:<4} "
                    f"{item._name:20} "
                    f"{item._price:<10} "
                    f"{item._available_stock:<17} "
                    f"{item.__caffeine_amount:<12} "
                    f"{temp_text:<8}"
                )

        print("=" * 90)


class Dessert(MenuItem):
    def __init__(self, item_id, name, price, available_stock, calories_kcal, storage_type):
        super().__init__(item_id, name, price, available_stock)
        self.__calories_kcal = calories_kcal
        self.__storage_type = storage_type

    @classmethod
    def display_list(cls):
        print()
        print("=" * 90)
        print("디저트 메뉴 목록".center(90))
        print("=" * 90)
        print(f"{'ID':<4} {'Name':20} {'Price':10} {'Available Stock':17} {'Calorie(kcal)':14} {'Storage Type'}")
        print("-" * 90)

        storage_dict = {1: "실온", 2: "냉장", 3: "냉동"}

        for item in MenuItem._menu_item_list:
            if isinstance(item, Dessert):
                print(
                    f"{item._id:<4} "
                    f"{item._name:20} "
                    f"{item._price:<10} "
                    f"{item._available_stock:<17} "
                    f"{item.__calories_kcal:<14} "
                    f"{storage_dict.get(item.__storage_type, '알수없음'):<8}"
                )

        print("=" * 90)


# 객체 생성 - Coffee 3개
menu1 = Coffee(1, "Americano", 3000, 10, 120, 1)
menu2 = Coffee(2, "Latte", 4000, 5, 90, 2)
menu3 = Coffee(3, "Mocha", 4500, 7, 150, 1)

# 객체 생성 - Dessert 3개
menu4 = Dessert(4, "Cheese Cake", 4500, 4, 350, 2)
menu5 = Dessert(5, "Macaron", 2500, 8, 180, 1)
menu6 = Dessert(6, "Tiramisu", 5000, 3, 420, 2)

# 출력
MenuItem.display_list()
Coffee.display_list()
Dessert.display_list()

+ Recent posts