내일배움캠프

데이터 분석 트랙 43일차 25.04.14. [TIL]

jjaio8986 2025. 4. 14. 20:42
  • 파이썬 과제 정리 [함수, 클래스]

  • 파이썬 [함수] 실습 문제

<문제1> : 가변 인자를 활용한 평균 계산기

 - 설명 : 여러 개의 숫자를 입력받아 평균을 계산하는 함수 calculate_average를 작성

 - 요구사항

1. 숫자가 하나도 전달되지 않았을 경우 "입력된 숫자가 없습니다."라는 메시지를 반환

2. 입력된 모든 숫자의 평균을 계산하여 소수점 2자리까지 반올림한 결과를 반환

3. 함수 설명을 위한 적절한 docstring을 포함

 

- 구현 코드

def calculate_average(*nums) :
    """
    전달받은 숫자의 평균을 반환하는 함수
   
    *nums : 가변 위치 인자로 임의의 개수를 인자로 받고, 함수 내에서 튜플로 처리된다.

    len(nums)를 통해 빈 튜플인지 확인하여 입력 숫자가 없을 때 문장을 출력한다.

    다음 순서로 total과 average에 각각의 연산값을 담아 round함수를 통해 소수점 1자리 까지만 표현한다.
    """
    if len(nums) == 0:
        return "입력된 숫자가 없습니다."
    total = sum(nums)
    average = total / len(nums)
    return round(average, 2)
   
print(calculate_average())

 

<문제2> : 키워드 인자를 활용한 학생 정보 출력

 - 설명 : 학생의 정보를 입력받아 형식에 맞게 출력하는 함수 print_student_info를 작성

 - 요구사항

1. 기본적으로 학생 정보에 '이름'과 '나이'는 반드시 입력

2. '이름'이나 '나이'가 전달되지 않은 경우 "필수 정보가 누락되었습니다."라는 메시지를 반환

3.모든 학생 정보를 "키: 값" 형태로 한 줄씩 출력하고, 마지막에 총 정보 개수를 반환

 - 구현 코드

def print_student_info(**info) :
    """
    학생 정보를 딕셔너리 형태로 반환하는 함수이다.

    **info를 통해 임의의 키워드 인자를 딕셔너리 형태로 담을 것이다.

    '이름', '나이'가 info에 없는 경우(key값에 없는 경우) 문자열을 반환한다.

    info.items()를 출력 시 return없이 함수가 끝날 경우 None이 반환 따라서
    result에 값을 담아주어 표현한다. 이때 리스트 형식으로 담으면 출력상 []가 나올 수 있으니
    문자형으로 선언 후 결합하는 형식으로 담는다.
    """

    if  "이름" not in info.keys() or "나이" not in info.keys():
        return "필수 정보가 누락되었습니다."
   
    result = ""
    for key, value in info.items():
        result += f"- {key}: {value}\n"
    result += f"총 정보 개수: {len(info)}"
    return result

 

 <문제3> : 람다 함수와 sorted를 활용한 정렬

 - 설명 : 학생들의 성적 정보가 담긴 리스트를 다양한 기준으로 정렬하는 함수 sort_students를 작성

 - 요구사항

1. “학생 정보” 리스트와 “정렬 기준(criterion)”을 매개변수로 하는 함수 제작

2. “학생 정보”는 딕셔너리 형태로, '이름', '국어', '영어', '수학' 의 4개의 키가 있음.

3. “정렬 기준”은 '이름', '국어', '영어', '수학', '총점’ 으로 5개

4. 잘못된 “정렬 기준”이 입력되면 "잘못된 정렬 기준입니다."를 반환

 - 구현 코드

def sort_students(students, criterion):
    """
    1. 학생 정보 리스트와 정렬 기준(criterion)의 2개의 매개변수로 받는다.
   
    2. 학생 정보 리스트의 1개의 요소에는 딕셔너리 형태로 구성
     -'이름', '국어', '영어', '수학' 키와 그에 해당하는 점수를 벨류로 갖는다.
   
    3. 정렬 기준은 리스트의 키 값과 '총점'(키값의 벨류들의 합)으로 총 5개

    4. 리스트 형태로 정렬된 값을 반환한다.   이때 정렬 기준이 잘못되면 문자열 반환
    """

    if criterion not in ('이름', '국어', '영어', '수학', '총점'):
        return "잘못된 정렬 기준입니다."

    if criterion == '총점':
        return sorted(students, key=lambda X: X['국어'] + X['영어'] + X['수학'])
    else:
        return sorted(students, key=lambda X: X[criterion])

students = [
    {'이름': '김철수', '국어': 90, '영어': 80, '수학': 85},
    {'이름': '이영희', '국어': 85, '영어': 95, '수학': 90},
    {'이름': '박민수', '국어': 95, '영어': 85, '수학': 75}
]

print(sort_students(students, '이름'))
print(sort_students(students, '총점'))
print(sort_students(students, '과학'))

 

<문제4> : 클로저 내부 함수와 nonlocal을 활용한 카운터 함수

 - 설명 : 호출 횟수를 카운트하면서 메시지를 출력하는 함수 create_counter를 작성

 - 요구사항

1. 문자열 `message`를 매개변수 매개변수로 하는 함수 제작

2. 함수 내부에 `counter` 함수를 정의하고, 이를 반환

3. `counter` 함수는 호출될 때마다 호출 횟수와 함께 전달된 메시지를 출력

4. `counter` 함수는 현재 호출 횟수를 반환

 - 구현 코드

def create_counter(message):
    """
    create_counter 함수는 message 를 매개변수로 받아 현재 호출 횟수와 메시지, 반환 횟수를 반환하는 외부 함수이다.
     - 카운팅을 위해 외부함수의 변수에 count를 선언 내부함수 counter를 호출하고 함수 내용을 종료한다.
   
    counter() 함수는 호출 횟수와 메시지, 반환 횟수를 반환하는 내부 함수이다.
     - print문으로 호출될 시 그 None 출력을 방지하기 위해 문자열 반환으로 함수를 종료한다.
   
    """
    count = 0
   
    def counter():
        nonlocal count
        count += 1
        return f"호출 {count}: {message} / 반환 : {count}"
       
    return counter

my_counter = create_counter("안녕하세요!")
print(my_counter())
print(my_counter())
print(my_counter())
print(my_counter())

  • 파이썬 [클래스] 실습 문제

 <문제1> : 기본 클래스 구현

 - 설명 : 클래스의 기본 구조와 인스턴스 생성

 - 요구사항

1.`Book` 클래스 제작

  > 클래스 속성 : “제목(title)”, “저자(author)”, “출판년도(year)”, “가격(price)”

2. 메서드 구현

  > `__init__`: 모든 속성을 초기화

  > `__str__`: "제목 (저자, 출판년도)" 형식의 문자열 반환

  > `get_age`: 현재 연도(2025)를 기준으로 책의 나이를 계산하여 반환

  > `apply_discount`: 인자로 할인율(%)을 받아, 할인된 가격을 반환

 - 구현 코드

class book:
    """
    book의 제목(title), 저자(author), 출판년도(year), 가격(price)를 속성으로 하는 클래스
    """
    def __init__(self, title, author, year, price):
        self.title = title
        self.author = author
        self.year = year
        self.price = price

    def __str__(self):
        """책 정보"""
        return f"책 정보 : {self.title}, ({self.author}, {self.year})"
   
    def get_age(self):
        """책 나이"""
        book_age = 2025 - self.year
        return f"책 나이 : {book_age}"
   
    def apply_dicount(self, discount_rate):
        """할인율(%) 적용 가격"""
        discount_price = self.price*(1-discount_rate/100)
        return round(discount_price, 2)
    pass
book1 = book("제목", "저자", 2020, 15000)
print(book1)
print(book1.get_age())
print("할인가:", book1.apply_dicount(50))

 

 <문제2> : 클래스 변수와 인스턴스 변수

 - 설명 : 클래스 변수와 인스턴스 변수의 차이 확인

 - 요구사항

  1. Student 클래스 구현

   - 클래스 변수 : `school_name`(학교명), `total_students`(총 학생 수)

   - 인스턴스 변수 : `name`(이름), `grade`(학년), `scores`(과목별 점수 딕셔너리)

  2. 다음의 메서드들을 구현

  - `__init__`: 이름과 학년을 초기화하고, `total_students`를 1 증가

  - `add_score`: 과목명과 점수를 받아 scores 딕셔너리에 추가

  - `get_average`: 모든 과목의 평균 점수 계산

  - `__str__`: "이름 (학년) - 평균: 평균점수" 형식의 문자열 반환

  - 클래스 메서드 `get_school_info`: 학교명과 총 학생 수 정보를 문자열로 반환

 

 - 구현 코드

class Student:
    """학교명은 직접 수정해야 합니다."""
    school_name = "???학교"
    total_students = 0
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade
        self.scores = {}
        Student.total_students += 1
   
    def add_score(self, subject, score):
        self.scores[subject] = score
   
    def get_average(self):
        if len(self.scores) == 0:
            return 0
        return sum(self.scores.values())/len(self.scores)

    def __str__(self):
        AVG = self.get_average()
        return f"{self.name} ({self.grade}학년) - 평균 : {AVG:.2f}"
   
    @classmethod
    def get_school_info(cls):
        return f"{cls.school_name} - 총 학생 수: {cls.total_students}명"

 

<문제3> : 특수 메서드 구현

 - 설명 : 특수 메서드 활용

 - 요구사항

  1. Vector2D 클래스 구현

    - 2차원 벡터의 x, y 좌표를 저장

  2. 특수 메서드 구현

   - `__init__`: x, y 좌표 초기화

   - `__str__`: "(x, y)" 형식의 문자열 반환

   - `__repr__`: "Vector2D(x, y)" 형식의 문자열 반환

   - `__add__`: 두 벡터의 덧셈 (좌표별 덧셈)

   - `__sub__`: 두 벡터의 뺄셈 (좌표별 뺄셈)

   - `__mul__`: 벡터와 스칼라의 곱셈 (각 좌표에 스칼라 곱)

   - `__eq__`: 두 벡터가 같은지 비교 (x, y 좌표가 모두 같아야 함)

   - `__len__`: 벡터의 길이(원점에서의 거리)를 정수로 반환

 

 - 구현 코드

""" 좌표별 연산의 경우 x1과 x2가 둘 중 누가 클지 모름
그러나 결국 연산이라는 점, 그 위치가 어디로 들어가도 동일한 값이 나와야 한다는 점에서
type_error보다 NorImplemented를 사용하여 연산 방향이 X1 -> X2 로 하다 연산오류일 시
X2 -> X1으로 자동으로 바꿔준다!
"""
class Vector2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y
   
    def __str__(self):
        return f"({self.x}, {self.y})"
   
    def __repr__(self):
        return f"Vector2D({self.x}, {self.y})"
   
    def __add__(self, other):
        if isinstance(other, Vector2D):
            return Vector2D(self.x + other.x, self.y + other.y)
        return NotImplemented
   
    def __sub__(self, other):
        if isinstance(other, Vector2D):
            return Vector2D(self.x - other.x, self.y - other.y)
        return NotImplemented

    def __mul__(self, scalar):
        if isinstance(scalar, (int, float)):
            return Vector2D(self.x * scalar, self.y * scalar)
        return NotImplemented

    def __eq__(self, other):
        if isinstance(other, Vector2D):
            return self.x == other.x and self.y == other.y
        return False

    def __len__(self):
        return int(((self.x**2)+(self.y**2))**0.5)  

 

<문제4> : 클래스 상속

 - 설명 : 클래스 상속과 메서드 오버라이딩

 - 요구사항

1. `Shape` 클래스를 상속받는 `Circle`(원) 클래스를 구현

  - 추가 속성: `radius`(반지름)

  - 오버라이드 메서드

  A. `__init__`: 부모 초기화 + 반지름 초기화

  B. `area`: 원의 넓이 계산 (π × r²)

  C. `perimeter`: 원의 둘레 계산 (2 × π × r) 

  D. `describe`: "이것은 반지름이 r인 원입니다." 형식 문자열 반환

 

2. `Shape` 클래스를 상속받는 `Rectangle`(직사각형) 클래스를 구현

  - 추가 속성: `width`(너비), `height`(높이)

  - 오버라이드 메서드:

 A. `__init__`: 부모 초기화 + 너비와 높이 초기화

 B. `area`: 직사각형의 넓이 계산 (너비 × 높이)

 C. `perimeter`: 직사각형의 둘레 계산 (2 × (너비 + 높이))

 D. `describe`: "이것은 너비가 width, 높이가 height인 직사각형입니다." 형식 문자열 반환

 - 추가 메서드:

 A. `is_square`: 정사각형인지 확인 (너비와 높이가 같으면 True, 다르면 False 반환)

 

3. `Rectangle` 클래스를 상속받는 `Cuboid`(직육면체) 클래스를 구현

  - 추가 속성: `depth`(깊이)

  - 오버라이드 메서드

  A. `__init__`: 부모 초기화 + 깊이 초기화 

  B. `area`: 직육면체의 표면적 계산 (2 × (너비 × 높이 + 너비 × 깊이 + 높이 × 깊이)) 

  C. `describe`: "이것은 너비가 width, 높이가 height, 깊이가 depth인 직육면체입니다." 형식 문자열 반환

  - 추가 메서드

  A. `volume`: 직육면체의 부피 계산 (너비 × 높이 × 깊이) 

  B.`is_cube`: 정육면체인지 확인 (너비, 높이, 깊이가 모두 같으면 True, 다르면 False 반환)

 

<기본 클래스(Shape) 코드>

import math

class Shape:
    """도형을 표현하는 기본 클래스"""
   
    def __init__(self, name):
        """도형 초기화"""
        self.name = name
   
    def area(self):
        """도형의 넓이를 계산"""
        return 0
   
    def perimeter(self):
        """도형의 둘레(또는 표면적)를 계산"""
        return 0
   
    def describe(self):
        """도형에 대한 설명"""
        return f"이것은 {self.name} 도형입니다."
   
    def __str__(self):
        """도형의 문자열 표현"""
        return f"{self.name} (면적: {self.area()}, 둘레: {self.perimeter()})"

 

<코드 구현1 : Circle 클래스>

# 1. Circle 클래스
class Circle(Shape):
    def __init__(self, radius):
        super().__init__("원")
        self.radius = radius
   
    def area(self):
        return math.pi * self.radius**2
   
    def perimeter(self):
        return 2 * math.pi * self.radius
   
    def describe(self):
        return f"이것은 반지름이 {self.radius}인 원입니다."

 

<코드 구현2 : Rectangle 클래스>

# 2. Rectangle 클래스
class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__("직사각형")
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

    def describe(self):
        return f"이것은 너비가 {self.width}, 높이가 {self.height}인 직사각형입니다."

    def is_square(self):
        return self.width == self.height

 

<코드 구현3 : Cuboid 클래스>

# 3. Cuboid 클래스
class Cuboid(Rectangle):
    def __init__(self, width, height, depth):
        super().__init__(width, height)
        self.name = "직육면체"  # Shape 클래스의 name 재설정
        self.depth = depth

    def area(self):
        w, h, d = self.width, self.height, self.depth
        return 2 * (w * h + w * d + h * d)

    def describe(self):
        return f"이것은 너비가 {self.width}, 높이가 {self.height}, 깊이가 {self.depth}인 직육면체입니다."

    def volume(self):
        return self.width * self.height * self.depth

    def is_cube(self):
        return self.width == self.height == self.depth