본문 바로가기

클라우드(Cloud)

[스나이퍼팩토리] 카카오클라우드 AIaaS 마스터 클래스 9주차 - 파이썬 심화 1 - 예제 코드 (Python Advanced 1 - Example Code)

 

 

 이렇게 함수, 모듈과 패키지, 객체 지향에 대한 개념까지 정리하며 살펴보았다. 이제는 위 개념들을 예제 코드를 통해 어떻게 사용하는 것인지를 자세히 알아볼 것이다.

 

Q1. 다음 리스트에서 홀수만 추출하여 제곱한 결과를 반환하시오. 

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 

# 방법 1: 반복문과 조건문 사용
result = [i ** 2 for i in numbers if i % 2 != 0] # 리스트 컴프리헨션 사용
print(result)

# 방법 2: lambda와 map, filter 등 사용
result = list(map(lambda x: x ** 2, filter(lambda x: x % 2 != 0, numbers)))
print(result)

 

 해당 문제를 보면 자연스럽게 반복문을 통해 numbers 리스트를 순회하며 2로 나누어지는지를 확인하는 조건문을 사용(방법 1) 해야겠다고 생각할 것이다. 그래서 처음엔 이 방식으로 코드를 작성하였으나, 위에서 배운 함수의 개념을 사용해야겠다는 생각을 하고 lambda 함수와 map, filter 등을 사용(방법 2) 해서 문제를 해결해보았다. 

 

 위 문제 해결에 사용한 함수 등을 간단히 설명하면, 

 

1. filter(function, iterable)

  • 역할: iterable에서 조건을 만족하는 요소만 걸러냄
  • function: 각 요소에 적용할 함수, True를 반환하는 요소만 통과됨

2. lambda 함수

  • 역할: 이름 없이 일회용으로 사용하는 간단한 함수 정의 방식
  • 문법: lambda 매개변수: 표현식

3. map(function, iterable)

  • 역할: iterable의 각 요소에 함수를 적용해서 새로운 값을 생성
  • function: 요소 하나를 받아서 처리하는 함수
  • 결과: 모든 요소에 함수 적용한 값을 담은 map 객체

4. list(...)

  • 역할: map이나 filter가 반환하는 이터레이터 객체 리스트로 변환
  • 필수는 아니지만, 결과를 보기 위해 리스트로 바꿔주는 게 일반적

 

Q2. 함수 호출 시 매개변수와 반환값을 로그로 출력하는 데코레이터를 작성하시오. 그리고 이 데코레이터를 add 함수에 적용하시오. 

# 함수 호출 시 매개변수와 반환값을 로그로 출력하는 데코레이터를 작성하시오. 그리고 이 데코레이터를 add 함수에 적용하시오. 

def log_function_call(func): # 데코레이터 함수 생성 / 인자로 func(데코레이터가 적용될 함수)를 받음 => add() 함수가 대상임 
    def wrapper(*args, **kwargs): # 실제로 add()를 감싸는 함수 / *args: 위치 인자 / **kwargs: 키워드 인자 
        print(f"함수 '{func.__name__}' 호출됨") # 데코레이터가 감싼 함수의 이름을 가져옴 
        print(f" - 인자 args: {args}")
        print(f" - 인자 kwargs: {kwargs}")

        result = func(*args, **kwargs) # 원래의 add() 함수를 실제로 실행 시킴 / 인자는 *args, **kwargs로 전달됨 

        print(f"반환값: {result}\n")
        return result
    
    return wrapper

@log_function_call
def add(a, b):
    return a + b

add(3, 5)

 

 데코레이터를 자주 사용해본 적은 없지만 사용해야하는 경우가 있을 수 있으니 알아두긴 해야할 것 같다. 아래에 위 코드의 자세한 설명을 적어보았다. 

 

1. log_function_call(func)

  • 이 함수가 데코레이터이다. 
  • 인자로 func (즉, 데코레이터가 적용될 함수)를 받음.
  • 여기서는 add() 함수가 그 대상이다. 

2. def wrapper(*args, **kwargs):

  • wrapper()는 실제로 add()를 감싸는 함수
  • *args는 위치 인자 (예: add(3, 5)에서 3, 5)
  • **kwargs는 키워드 인자 (예: add(a=3, b=5)처럼 명시된 인자)

3. print(f"함수 '{func.__name__}' 호출됨")

  • func.__name__는 데코레이터가 감싼 함수의 이름 (add)을 가져옴
  • 어떤 함수가 호출됐는지 로그 출력

4. result = func(*args, **kwargs)

  • 원래의 add() 함수를 실제로 실행시킴
  • 인자는 *args, **kwargs로 전달됨
  • 반환값은 result에 저장

5. return result

  • wrapper() 함수가 실행된 결과를 다시 원래처럼 반환해줌
  • 즉, 이 데코레이터를 써도 기존 add()의 동작은 그대로 유지된다. 

 

Q3. 간단한 계산기 모듈을 만들어보시오.

# calculator.py # 모듈로 사용할 파일 이름

def add(a, b):
  return a + b
  
def subtract(a, b):
  return a - b
  
def multiply(a, b):
  return a * b
  
def divide(a, b):
  if b == 0: 
    print("분모에 0을 사용할 수 없습니다.")
  return a / b

 

위와 같이 calculator.py 파일에 계산기 기능을 하는 함수들을 생성해서 저장해놓았다. 이 모듈을 사용하려면 다른 파이썬 파일에서 import를 사용하면 된다. 

 

사용 예시: 

# main.py

import calculator

print("덧셈: ", calculator.add(3, 5))
print("뺄셈: ", calculator.subtract(10, 4))
print("곱셈: ", calculator.multiply(6, 7))
print("나눗셈: ", calculator.divide(8, 2))
print("0으로 나누기: ", calculator.divide(10, 0))

 

 

Q4. 간단한 도서관 시스템 구현(문제의 자세한 설명은 코드 내 주석에 포함) 

# Day3

# 클래스와 객체지향 
# 과제: 도서관 시스템 구현 

# 다음 클래스를 구현하세요:
# • `Book`: 도서 정보(제목, 저자, ISBN, 출판연도 등)를 관리
# • `Library`: 도서 컬렉션을 관리하고 대출/반납 기능 제공
# • `Member`: 도서관 회원 정보와 대출 목록 관리

# • 다음 기능을 구현하세요:
# • 도서 추가/삭제
# • 도서 검색(제목, 저자, ISBN으로)
# • 도서 대출/반납
# • 회원 등록/관리
# • 회원별 대출 현황 확인

# • 객체 지향 설계 원칙(SOLID)을 최소한 2가지 이상 적용하세요.

# • 적절한 캡슐화를 통해 데이터를 보호하세요.

class Book: 
    def __init__(self, title, author, isbn, year):
        # Book 객체가 생성될 때, 아래 정보를 받아 초기화 
        self.__title = title 
        self.__author = author
        self.__isbn = isbn
        self.__year = year
        self.__is_borrowed = False # 도서 대출 상태 

    def get_title(self):
        return self.__title # 제목 
    
    def get_author(self):
        return self.__author # 저자 
    
    def get_isbn(self):
        return self.__isbn # ISBN(도서번호)
    
    def get_year(self):
        return self.__year # 출판연도 
    
    def is_borrowed(self):
        return self.__is_borrowed # 대출 여부 (대출되지 않은 상태가 기본값)
    
    def borrow(self): # 도서 대출 확인 
        if self.__is_borrowed: # True: 대출 중인 상태라면 아래 코드 실행 
            # 이미 대출 중이라면 예외 처리 
            raise Exception(f"'{self.__title}'은 대출 중입니다. ")
        
        self.__is_borrowed = True # 대출 상태 처리 

    def return_book(self): # 반납 처리 
        self.__is_borrowed = False # False: 대출 상태 해제


class Library:
    def __init__(self):
        self.__books = [] # 도서 목록을 저장하는 리스트 생성
        self.__members = {} # 회원 정보를 저장하는 딕셔너리 (key: 회원 ID, value: Member 객체)

    def add_book(self, book): # 도서 추가 
        self.__books.append(book)

    def delete_book(self, isbn): # ISBN을 기준으로 도서 삭제 
        # 리스트 컴프리헨션 사용 / 해당 ISBN이 아닌 책들을 새 리스트에 저장 (일치하지 않는 도서만 남기는 방식)
        self.__books = [book for book in self.__books if book.get_isbn() != isbn]

    def search_book(self, keyword, key="title"):
        # 제목을 기본 키로 도서를 검색 (기본값: 제목)
        if key == "title":
            # 제목에 keyword가 포함된 도서 리스트를 반환 
            return [b for b in self.__books if keyword.lower() in b.get_title().lower()]
        elif key == "author":
            # 저자명에 keyword가 포함된 책 반환 
            return [b for b in self.__books if keyword.lower() in b.get_author().lower()]
        elif key == "isbn":
            # ISBN이 정확히 일치하는 책 반환 
            return [b for b in self.__books if keyword == b.get_isbn()]
        else:
            return []
        
    def register_member(self, member): # 새로운 회원 등록 
        # member 딕셔너리에 member_id를 키로 member 객체를 저장 
        self.__members[member.get_id()] = member

    def get_member(self, member_id):
        # member_id로 회원 반환 
        return self.__members.get(member_id)

    def borrow_book(self, member_id, isbn): # 도서 대출 
        member = self.get_member(member_id) # 회원 받아오기 
        book = next((b for b in self.__books if b.get_isbn() == isbn), None) # ISBN으로 도서 찾기 
        if member and book: # 회원과 도서가 모두 존재한다면
            member.borrow_book(book) # Member 클래스의 borrow_book이 실행되면서 도서가 대출됨 -> 도서 대출 처리가 위임되었다고 말함
    
    def return_book(self, member_id, isbn): # 도서 반납 
        member = self.get_member(member_id) # 회원 받아오기 

        if member: # 만약 회원이 있다면 
            # 해당 회원이 대출 중인 도서 중 ISBN이 일치하는 도서 찾기 
            book = next((b for b in member.get_borrowed_books() if b.get_isbn() == isbn), None)
            if book: # 만약 도서가 있다면 
                member.return_book(book) # 도서 반납 처리 위임 
    
    def print_member_status(self, member_id): # 회원의 대출 목록 출력 
        member = self.get_member(member_id) # 회원 받아오기 

        if member: # 만약 회원이 있다면 
            print(f"{member.get_name()}님의 대출 목록: ") 
            for book in member.get_borrowed_books(): # 대출 중인 도서를 순회하며 출력
                print(f" - {book.get_title()} by {book.get_author()}")

class Member:
    def __init__(self, name, member_id):
        self.__name = name # 회원 이름 
        self.__member_id = member_id # 회원 ID
        self.__borrowed_books = [] # 대출한 도서 저장할 리스트 생성 

    def get_name(self):
        return self.__name # 이름 반환 
    
    def get_id(self):
        return self.__member_id # 회원 ID 반환 
    
    def get_borrowed_books(self): 
        return self.__borrowed_books # 대출 중인 도서 리스트 반환 
    
    def borrow_book(self, book): # 도서 대출 함수 
        book.borrow() # Book 클래스에서 도서 상태 변경 처리 
        self.__borrowed_books.append(book) # 도서 대출 리스트에 추가 

    def return_book(self, book): # 도서 반납 함수 
        if book in self.__borrowed_books: # 만약에 책이 도서 대출 리스트에 있다면 
            book.return_book() # Book 클래스에서 도서 반납 처리 
            self.__borrowed_books.remove(book) # 리스트 목록에서 제거

 

해당 과제는 사용자에게 CLI를 통해 입력받는 경우가 아니라 외부에서 객체를 생성해 추가하거나 하는 그런 경우를 가정한 코드로 작성하였다. 만약 사용자에게 CLI를 통해 정보를 입력받는 경우라고 한다면 위 코드에 메인 함수와 실행 함수 등의 코드를 추가해주면 된다. 다른 코드나 프로그램에서 모듈로써 불러와서 기능을 사용하는 그런 코드에 가깝다고 생각하면 될 것 같다. 

 

 

 위 코드를 작성할 때, 각 클래스에 어떤 기능을 담당하는 함수들을 구현해야하는지를 고민했다. 왼편의 메모에서 볼 수 있듯이 예를 들면, 'Book' 클래스에서 책이 대출 중인지를 확인하는 함수가 필요한가? 라는 생각을 했다. 대출/반납 기능은 'Library' 클래스에 포함되어 있고, 'Member' 클래스에도 대출중인 도서 리스트와 대출/반납 기능을 담당한느 기능이 있기에 그런 생각을 했었던 것 같다. 

 

 하지만 조금 더 생각해보니 대출/반납을 처리한 후 'Book' 클래스에서 이를 확인하는 기능과 그에 대한 정보가 없다면 중복의 문제가 발생할 수도 있을 것이라 생각했고, 그렇게 추가하게 되었다. 

 

 

 

 

 

 

 

 

 

 

 

 


본 후기는 [카카오엔터프라이즈x스나이퍼팩토리] 카카오클라우드로 배우는 AIaaS 마스터 클래스 (B-log) 리뷰로 작성 되었습니다.