WeniVooks

검색

파이썬 클래스 톺아보기

연습문제

연습문제 - 온라인 서점 시스템 구현하기

다음 문제들은 온라인 서점의 도서 관리 시스템을 구현하는 과제입니다.

도서 정보 관리, 전자책 기능, 할인 정책 등 실제 서점 운영에 필요한 다양한 기능을 배운 내용을 바탕으로 구현해보세요.

1번 문제

book_data = [
    {
        "isbn": "123-4-56-789012-3",
        "title": "파이썬 클래스 톺아보기",
        "author": "라이캣",
        "price": 25000,
        "stock": 10
    },
    {
        "isbn": "123-4-56-789012-4",
        "title": "JavaScript 베이스캠프",
        "author": "빙키",
        "price": 15000,
        "stock": 5
    }
]
book_data = [
    {
        "isbn": "123-4-56-789012-3",
        "title": "파이썬 클래스 톺아보기",
        "author": "라이캣",
        "price": 25000,
        "stock": 10
    },
    {
        "isbn": "123-4-56-789012-4",
        "title": "JavaScript 베이스캠프",
        "author": "빙키",
        "price": 15000,
        "stock": 5
    }
]

위 도서 데이터를 참고하여 Book 클래스를 만들어주세요.

  • ISBN(국제표준도서정보)은 private 속성으로 설정
  • price와 stock은 property를 사용하여 음수가 되지 않도록 구현
  • 매직메서드 str, eq(isbn 기준), lt(가격 기준) 구현

다음 테스트 코드가 동작해야 합니다.

book1 = Book("123-4-56-789012-3", "파이썬 클래스 톺아보기", "라이캣", 25000, 10)
book2 = Book("123-4-56-789012-4", "JavaScript 베이스캠프", "빙키", 15000, 5)
 
print(book1.price)  # 25000
book1.price = -5000  # 음수 price 설정 시도(에러발생)
print(book1.price)  # 25000 (음수가 되지 않음)
 
print(book1.stock)  # 10
book1.stock = -3  # 음수 stock 설정 시도(에러발생)
print(book1.stock)  # 10 (음수가 되지 않음)
 
print(book1)  # <파이썬 클래스 톺아보기> 저자: 라이캣, 가격: 25000원, 재고: 10권
print(book1 == book2)  # False
print(book1 < book2)  # False
book1 = Book("123-4-56-789012-3", "파이썬 클래스 톺아보기", "라이캣", 25000, 10)
book2 = Book("123-4-56-789012-4", "JavaScript 베이스캠프", "빙키", 15000, 5)
 
print(book1.price)  # 25000
book1.price = -5000  # 음수 price 설정 시도(에러발생)
print(book1.price)  # 25000 (음수가 되지 않음)
 
print(book1.stock)  # 10
book1.stock = -3  # 음수 stock 설정 시도(에러발생)
print(book1.stock)  # 10 (음수가 되지 않음)
 
print(book1)  # <파이썬 클래스 톺아보기> 저자: 라이캣, 가격: 25000원, 재고: 10권
print(book1 == book2)  # False
print(book1 < book2)  # False

2번 문제

다음 추상 클래스와 Enum을 활용하여 도서관리 시스템을 구현하세요.

from abc import ABC, abstractmethod
from enum import Enum
 
class BookFormat(Enum):
    PAPER = "종이책"
    EBOOK = "전자책" 
    AUDIO = "오디오북"
from abc import ABC, abstractmethod
from enum import Enum
 
class BookFormat(Enum):
    PAPER = "종이책"
    EBOOK = "전자책" 
    AUDIO = "오디오북"
  • Book을 추상 클래스로 변경하고, calculate_discount() 추상 메서드를 포함하도록 수정하세요.
  • 각 도서 형식별로 할인율을 다르게 적용하세요.
  • 종이책: 10% 할인
  • 전자책: 20% 할인
  • 오디오북: 15% 할인

다음 테스트 코드가 동작해야 합니다.

paper_book = PaperBook("123-4-56-789012-3", "파이썬 클래스 톺아보기", "라이캣", 25000, 10)
ebook = EBook("123-4-56-789012-4", "JavaScript 베이스캠프", "빙키", 15000, 5)
audio_book = AudioBook("123-4-56-789012-5", "Python 베이스캠프", "뮤라", 12000, 50)
 
print(paper_book.calculate_discount())  # 22500 (10% 할인)
print(ebook.calculate_discount())      # 12000 (20% 할인)
print(audio_book.calculate_discount()) # 10200 (15% 할인)
paper_book = PaperBook("123-4-56-789012-3", "파이썬 클래스 톺아보기", "라이캣", 25000, 10)
ebook = EBook("123-4-56-789012-4", "JavaScript 베이스캠프", "빙키", 15000, 5)
audio_book = AudioBook("123-4-56-789012-5", "Python 베이스캠프", "뮤라", 12000, 50)
 
print(paper_book.calculate_discount())  # 22500 (10% 할인)
print(ebook.calculate_discount())      # 12000 (20% 할인)
print(audio_book.calculate_discount()) # 10200 (15% 할인)

3번 문제

다음 데이터클래스를 상속받아 도서 클래스를 구현하세요.

from dataclasses import dataclass
 
@dataclass
class BookInfo:
    title: str
    author: str
    publisher: str
from dataclasses import dataclass
 
@dataclass
class BookInfo:
    title: str
    author: str
    publisher: str

각 클래스는 다음 특성을 가져야 합니다.

  • PhysicalBook: weight(무게), size(크기) 추가
  • EBook: format_type(파일형식), download_url(다운로드 URL) 추가

다음 테스트 코드가 동작해야 합니다.

physical_book = PhysicalBook(
    title="파이썬 클래스 톺아보기",
    author="라이캣",
    publisher="위니북스",
    weight=0.780,  # kg
    size="188*240*35"  # mm
)
 
digital_book = EBook(
    title="JavaScript 베이스캠프",
    author="빙키",
    publisher="위니북스",
    format_type="EPUB",
    download_url="https://example.com/download"
)
 
print(physical_book.title)  # 파이썬 클래스 톺아보기
print(physical_book.weight)  # 0.780
print(physical_book.size)   # 188*240*35
 
print(digital_book.title)  # JavaScript 베이스캠프
print(digital_book.format_type)  # EPUB
print(digital_book.download_url)  # https://example.com/download
physical_book = PhysicalBook(
    title="파이썬 클래스 톺아보기",
    author="라이캣",
    publisher="위니북스",
    weight=0.780,  # kg
    size="188*240*35"  # mm
)
 
digital_book = EBook(
    title="JavaScript 베이스캠프",
    author="빙키",
    publisher="위니북스",
    format_type="EPUB",
    download_url="https://example.com/download"
)
 
print(physical_book.title)  # 파이썬 클래스 톺아보기
print(physical_book.weight)  # 0.780
print(physical_book.size)   # 188*240*35
 
print(digital_book.title)  # JavaScript 베이스캠프
print(digital_book.format_type)  # EPUB
print(digital_book.download_url)  # https://example.com/download

4번 문제

다음 두 클래스를 함께 상속받는 EBook 클래스를 구현하세요.

class Book:
    def __init__(self, title, price):
        self._title = title
        self._price = price
        self._purchased = False
    
    def purchase(self):
        if self._purchased:
            return "이미 구매한 도서입니다."
        self._purchased = True
        return "구매가 완료되었습니다."
 
class Downloadable:
    def __init__(self):
        self._download_count = 0
        self._max_downloads = 2
    
    def download(self):
        if not hasattr(self, '_purchased') or not self._purchased:
            return "구매 후 다운로드 가능합니다."
        if self._download_count >= self._max_downloads:
            return "다운로드 횟수를 초과했습니다."
        self._download_count += 1
        return f"다운로드 완료 (남은 횟수: {self._max_downloads - self._download_count})"
class Book:
    def __init__(self, title, price):
        self._title = title
        self._price = price
        self._purchased = False
    
    def purchase(self):
        if self._purchased:
            return "이미 구매한 도서입니다."
        self._purchased = True
        return "구매가 완료되었습니다."
 
class Downloadable:
    def __init__(self):
        self._download_count = 0
        self._max_downloads = 2
    
    def download(self):
        if not hasattr(self, '_purchased') or not self._purchased:
            return "구매 후 다운로드 가능합니다."
        if self._download_count >= self._max_downloads:
            return "다운로드 횟수를 초과했습니다."
        self._download_count += 1
        return f"다운로드 완료 (남은 횟수: {self._max_downloads - self._download_count})"

EBook은 다음 특성을 가져야 합니다.

  • Book과 Downloadable 다중 상속
  • format_type(파일형식: EPUB/PDF) 속성 추가
  • 가격이 20000원 이상인 경우 최대 다운로드 횟수 3회로 증가
  • PDF 형식의 경우 가격 10% 할증

다음 테스트 코드가 동작해야 합니다.

ebook = EBook("파이썬 기초", 25000, "PDF")
print(ebook.purchase())  # "구매가 완료되었습니다."
print(ebook.download())  # "다운로드 완료 (남은 횟수: 2)"
print(ebook.download())  # "다운로드 완료 (남은 횟수: 1)"
print(ebook._price)      # 27500
ebook = EBook("파이썬 기초", 25000, "PDF")
print(ebook.purchase())  # "구매가 완료되었습니다."
print(ebook.download())  # "다운로드 완료 (남은 횟수: 2)"
print(ebook.download())  # "다운로드 완료 (남은 횟수: 1)"
print(ebook._price)      # 27500

5번 문제

다음 클래스의 메서드들을 구현하세요.

class Book(ABC):
    _total_books = 0
    _daily_sales = {}  # 날짜별 판매량 기록
class Book(ABC):
    _total_books = 0
    _daily_sales = {}  # 날짜별 판매량 기록

1. 클래스 메서드

  • total_books(): 전체 도서 수 반환
  • get_sales_by_date(date): 특정 날짜의 총 판매량 반환
  • record_sale(date): 판매 기록 추가
  • get_best_selling_date(): 가장 판매량이 많았던 날짜 반환

2. 정적 메서드

  • calculate_bulk_discount(quantity): 대량 구매 시 할인율 계산
    • 100권 이상: 20% 할인
    • 50권 이상: 15% 할인
    • 20권 이상: 10% 할인
  • validate_isbn(isbn): ISBN 유효성 검사 (13자리가 맞는지, 숫자로만 이루어져 있는지 검사해야 합니다.)
  • is_workday(date): 영업일 여부 확인 (주말, 공휴일은 제외합니다.)

다음 테스트 코드가 동작해야 합니다.

book = Book("123-4-56-789012-3", "파이썬 클래스 톺아보기", "라이캣", 25000, 10)
 
# 영업일 확인
print(Book.is_workday("2025-02-09"))  # False (일요일)
print(Book.is_workday("2025-02-10"))  # True (월요일)
 
# 판매 기록 추가
print(Book.record_sale("2025-02-09"))  # "영업일이 아닙니다."
print(Book.record_sale("2025-02-10"))  # "판매 기록이 추가되었습니다."
print(Book.record_sale("2025-02-10"))  # "판매 기록이 추가되었습니다."
 
# 판매량 확인
print(Book.get_sales_by_date("2025-02-10"))  # 2
print(Book.get_best_selling_date())  # "2025-02-10"
 
# 대량 구매 할인율
print(Book.calculate_bulk_discount(100))  # 0.2
print(Book.calculate_bulk_discount(30))   # 0.1
book = Book("123-4-56-789012-3", "파이썬 클래스 톺아보기", "라이캣", 25000, 10)
 
# 영업일 확인
print(Book.is_workday("2025-02-09"))  # False (일요일)
print(Book.is_workday("2025-02-10"))  # True (월요일)
 
# 판매 기록 추가
print(Book.record_sale("2025-02-09"))  # "영업일이 아닙니다."
print(Book.record_sale("2025-02-10"))  # "판매 기록이 추가되었습니다."
print(Book.record_sale("2025-02-10"))  # "판매 기록이 추가되었습니다."
 
# 판매량 확인
print(Book.get_sales_by_date("2025-02-10"))  # 2
print(Book.get_best_selling_date())  # "2025-02-10"
 
# 대량 구매 할인율
print(Book.calculate_bulk_discount(100))  # 0.2
print(Book.calculate_bulk_discount(30))   # 0.1

답안

class Book:
    def __init__(self, isbn, title, author, price, stock):
        self._isbn = isbn
        self.title = title
        self.author = author
        self._price = price
        self._stock = stock
    
    @property
    def price(self):
        return self._price
    
    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError("가격은 음수가 될 수 없습니다.")
        self._price = value
    
    @property
    def stock(self):
        return self._stock
    
    @stock.setter
    def stock(self, value):
        if value < 0:
            raise ValueError("재고는 음수가 될 수 없습니다.")
        self._stock = value
    
    def __str__(self):
        return f"<{self.title}> 저자: {self.author}, 가격: {self.price}원, 재고: {self.stock}권"
    
    def __eq__(self, other):
        if isinstance(other, Book):
            return self._isbn == other._isbn
        return False
    
    def __lt__(self, other):
        if isinstance(other, Book):
            return self.price < other.price
        return NotImplemented
 
# 사용 예시
books = [Book(**data) for data in book_data]
class Book:
    def __init__(self, isbn, title, author, price, stock):
        self._isbn = isbn
        self.title = title
        self.author = author
        self._price = price
        self._stock = stock
    
    @property
    def price(self):
        return self._price
    
    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError("가격은 음수가 될 수 없습니다.")
        self._price = value
    
    @property
    def stock(self):
        return self._stock
    
    @stock.setter
    def stock(self, value):
        if value < 0:
            raise ValueError("재고는 음수가 될 수 없습니다.")
        self._stock = value
    
    def __str__(self):
        return f"<{self.title}> 저자: {self.author}, 가격: {self.price}원, 재고: {self.stock}권"
    
    def __eq__(self, other):
        if isinstance(other, Book):
            return self._isbn == other._isbn
        return False
    
    def __lt__(self, other):
        if isinstance(other, Book):
            return self.price < other.price
        return NotImplemented
 
# 사용 예시
books = [Book(**data) for data in book_data]
from abc import ABC, abstractmethod
from enum import Enum
 
class BookFormat(Enum):
    PAPER = "종이책"
    EBOOK = "전자책" 
    AUDIO = "오디오북"
 
    def get_discount_rate(self):
        discount_rates = {
            BookFormat.PAPER: 0.1,
            BookFormat.EBOOK: 0.2,
            BookFormat.AUDIO: 0.15
        }
        return discount_rates[self]
 
class Book(ABC):
    def __init__(self, isbn, title, author, price, stock, format_type: BookFormat):
        self._isbn = isbn
        self.title = title
        self.author = author
        self.price = price
        self.stock = stock
        self.format_type = format_type
    
    def calculate_discount(self):
        return int(self.price * (1-self.format_type.get_discount_rate()))
 
class PaperBook(Book):
    def __init__(self, isbn, title, author, price, stock):
        super().__init__(isbn, title, author, price, stock, BookFormat.PAPER)
 
class EBook(Book):
    def __init__(self, isbn, title, author, price, stock):
        super().__init__(isbn, title, author, price, stock, BookFormat.EBOOK)
 
class AudioBook(Book):
    def __init__(self, isbn, title, author, price, stock):
        super().__init__(isbn, title, author, price, stock, BookFormat.AUDIO)
 
from abc import ABC, abstractmethod
from enum import Enum
 
class BookFormat(Enum):
    PAPER = "종이책"
    EBOOK = "전자책" 
    AUDIO = "오디오북"
 
    def get_discount_rate(self):
        discount_rates = {
            BookFormat.PAPER: 0.1,
            BookFormat.EBOOK: 0.2,
            BookFormat.AUDIO: 0.15
        }
        return discount_rates[self]
 
class Book(ABC):
    def __init__(self, isbn, title, author, price, stock, format_type: BookFormat):
        self._isbn = isbn
        self.title = title
        self.author = author
        self.price = price
        self.stock = stock
        self.format_type = format_type
    
    def calculate_discount(self):
        return int(self.price * (1-self.format_type.get_discount_rate()))
 
class PaperBook(Book):
    def __init__(self, isbn, title, author, price, stock):
        super().__init__(isbn, title, author, price, stock, BookFormat.PAPER)
 
class EBook(Book):
    def __init__(self, isbn, title, author, price, stock):
        super().__init__(isbn, title, author, price, stock, BookFormat.EBOOK)
 
class AudioBook(Book):
    def __init__(self, isbn, title, author, price, stock):
        super().__init__(isbn, title, author, price, stock, BookFormat.AUDIO)
 
@dataclass
class PhysicalBook(BookInfo):
    weight: float
    size: tuple
    
@dataclass
class EBook(BookInfo):
    format_type: str
    download_url: str
@dataclass
class PhysicalBook(BookInfo):
    weight: float
    size: tuple
    
@dataclass
class EBook(BookInfo):
    format_type: str
    download_url: str
class EBook(Book, Downloadable):
    def __init__(self, title, price, format_type):
        # 부모 클래스 초기화
        Book.__init__(self, title, price)
        Downloadable.__init__(self)
        
        self._format_type = format_type
        
        # 가격이 20000원 이상이면 다운로드 횟수 증가
        if price >= 20000:
            self._max_downloads = 3
            
        # PDF 형식이면 가격 10% 할증
        if format_type == "PDF":
            self._price = int(self._price * 1.1)
    
    @property
    def format_type(self):
        return self._format_type
        
    @property
    def price(self):
        return self._price
class EBook(Book, Downloadable):
    def __init__(self, title, price, format_type):
        # 부모 클래스 초기화
        Book.__init__(self, title, price)
        Downloadable.__init__(self)
        
        self._format_type = format_type
        
        # 가격이 20000원 이상이면 다운로드 횟수 증가
        if price >= 20000:
            self._max_downloads = 3
            
        # PDF 형식이면 가격 10% 할증
        if format_type == "PDF":
            self._price = int(self._price * 1.1)
    
    @property
    def format_type(self):
        return self._format_type
        
    @property
    def price(self):
        return self._price
from abc import ABC, abstractmethod
from datetime import datetime
 
class Book(ABC):
    _total_books = 0
    _daily_sales = {}  # 날짜별 판매량 기록
    
    def __init__(self, isbn, title, author, price, stock):
        self._isbn = isbn
        self.title = title
        self.author = author
        self.price = price
        self.stock = stock
        Book._total_books += 1
    
    @classmethod
    def total_books(cls):
        """전체 도서 수 반환"""
        return cls._total_books
    
    @classmethod
    def get_sales_by_date(cls, date):
        """특정 날짜의 총 판매량을 반환"""
        return cls._daily_sales.get(date, 0)
    
    @classmethod
    def record_sale(cls, date):
        """판매 기록"""
        if not cls.is_workday(date):
            return "영업일이 아닙니다."
        cls._daily_sales[date] = cls._daily_sales.get(date, 0) + 1
        return "판매 기록이 추가되었습니다."
    
    @classmethod
    def get_best_selling_date(cls):
        """가장 판매량이 많았던 날짜 반환"""
        if not cls._daily_sales:
            return None
        return max(cls._daily_sales.items(), key=lambda x: x[1])[0]
    
    @staticmethod
    def calculate_bulk_discount(quantity):
        """대량 구매 시 할인율 계산"""
        if quantity >= 100:
            return 0.2
        elif quantity >= 50:
            return 0.15
        elif quantity >= 20:
            return 0.1
        return 0
    
    @staticmethod
    def validate_isbn(isbn):
        """ISBN 유효성 검사"""
        isbn = isbn.replace('-', '')
        return isbn.isdigit() and len(isbn) == 13
    
    @staticmethod
    def is_workday(date_str):
        """영업일 여부 확인"""
        try:
            date = datetime.strptime(date_str, '%Y-%m-%d').date()
            # 주말 체크
            if date.weekday() >= 5:  # 5는 토요일, 6은 일요일
                return False
            return True
        except ValueError:
            return False
 
from abc import ABC, abstractmethod
from datetime import datetime
 
class Book(ABC):
    _total_books = 0
    _daily_sales = {}  # 날짜별 판매량 기록
    
    def __init__(self, isbn, title, author, price, stock):
        self._isbn = isbn
        self.title = title
        self.author = author
        self.price = price
        self.stock = stock
        Book._total_books += 1
    
    @classmethod
    def total_books(cls):
        """전체 도서 수 반환"""
        return cls._total_books
    
    @classmethod
    def get_sales_by_date(cls, date):
        """특정 날짜의 총 판매량을 반환"""
        return cls._daily_sales.get(date, 0)
    
    @classmethod
    def record_sale(cls, date):
        """판매 기록"""
        if not cls.is_workday(date):
            return "영업일이 아닙니다."
        cls._daily_sales[date] = cls._daily_sales.get(date, 0) + 1
        return "판매 기록이 추가되었습니다."
    
    @classmethod
    def get_best_selling_date(cls):
        """가장 판매량이 많았던 날짜 반환"""
        if not cls._daily_sales:
            return None
        return max(cls._daily_sales.items(), key=lambda x: x[1])[0]
    
    @staticmethod
    def calculate_bulk_discount(quantity):
        """대량 구매 시 할인율 계산"""
        if quantity >= 100:
            return 0.2
        elif quantity >= 50:
            return 0.15
        elif quantity >= 20:
            return 0.1
        return 0
    
    @staticmethod
    def validate_isbn(isbn):
        """ISBN 유효성 검사"""
        isbn = isbn.replace('-', '')
        return isbn.isdigit() and len(isbn) == 13
    
    @staticmethod
    def is_workday(date_str):
        """영업일 여부 확인"""
        try:
            date = datetime.strptime(date_str, '%Y-%m-%d').date()
            # 주말 체크
            if date.weekday() >= 5:  # 5는 토요일, 6은 일요일
                return False
            return True
        except ValueError:
            return False
 
{"packages":["numpy","pandas","matplotlib","lxml"]}
5.6 Dataclasses