Sparta/Theory

[250825] 스파르타코딩 본캠프 15일차 (1)

junecho 2025. 8. 25. 13:53

🟡 Python 5차 강의 🟡

 클래스                                                                  

🔰  class ?                                                                                                                 

  • 클래스(Class) : 객체(Object)를 만들기 위한 설계도나 틀. 속성(특징, 데이터)메서드(기능, 함수)로 구성
  • 객체(Object) : 클래스를 통해 만들어낸 실제 사물(Instance : 인스턴스). 클래스를 붕어빵 틀이라고 한다면, 객체는 그 틀로 찍어낸 각각의 붕어빵.
  • 인스턴스(Instance) : 특정 클래스로부터 생성된 객체를 그 클래스의 인스턴스라 부름

 

  • 클래스 이름은 관례적으로 단어의 첫 글자를 대문자로 시작 (예: MyClass).
  • 클래스 내부 요소
    • 인스턴스 변수(instance variable), 클래스 변수(class variable)
    • 인스턴스 메서드(instance method), 클래스 메서드(class method), 정적 메서드(static method)
class 클래스이름:
    # 클래스 본문
    # 속성(변수)과 메서드(함수)를 정의

 

 

__init__ 메서드 (생성자)

  • 인스턴스가 생성될 때 자동으로 호출되는 메서드로, 주로 인스턴스 변수를 초기화하는 데 사용
  • 생성자(Constructor) 라고도 함
class Person:
    def __init__(self, name, age):
        self.name = name  # 인스턴스 변수
        self.age = age

    def introduce(self):
        print(f"안녕하세요, 제 이름은 {self.name}이고, 나이는 {self.age}살입니다.")

Person 클래스는 __init__ 메서드를 통해 name과 age라는 인스턴스 변수를 설정하고, introduce() 메서드를 통해 자기 소개를 할 수 있게 함

 

 

객체 생성

클래스를 정의한 후에는 클래스를 호출하는 형식으로 객체를 생성

p1 = Person("Alice", 25)
p2 = Person("Bob", 30)

p1.introduce()  # "안녕하세요, 제 이름은 Alice이고, 나이는 25살입니다."
p2.introduce()  # "안녕하세요, 제 이름은 Bob이고, 나이는 30살입니다."

p1과 p2는 모두 Person 클래스의 인스턴스로, 각각 다른 속성을 가질 수 있음

 

 

변수

  • 인스턴스 변수 (instance variable)
    • 각 객체마다 별도로 관리되는 변수
    • self.name, self.age와 같이 self를 통해 참조하는 변수는 인스턴스마다 다른 값을 가짐
  • 클래스 변수 (class variable)
    • 클래스로부터 만들어진 모든 인스턴스가 공유하는 변수
    • 클래스 블록 내에서 self 없이 바로 변수를 정의하면 클래스 변수로 설정
class Car:
    wheels = 4  # 클래스 변수: 모든 Car 인스턴스는 바퀴가 4개

    def __init__(self, color):
        self.color = color  # 인스턴스 변수: 각 차마다 색이 다를 수 있음

car1 = Car("red")
car2 = Car("blue")

print(car1.color)  # "red" (car1만의 인스턴스 변수)
print(car2.color)  # "blue" (car2만의 인스턴스 변수)

print(car1.wheels) # 4 (클래스 변수는 모든 객체가 공유)
print(car2.wheels) # 4

wheels는 클래스 변수로 Car의 모든 인스턴스에 동일한 값(4개)을 갖음.

반면 color는 인스턴스별로 다른 값(red, blue)을 갖음



 


🔰 메서드
                                                                                                                

인스턴스 메서드 (Instance Method)

  • 첫 번째 매개변수로 self를 받음
  • 각 인스턴스에 대해서 동작하며, 인스턴스 변수를 다루는 데 주로 사용
class Person:
    def __init__(self, name):
        self.name = name

    def say_hello(self):
        print(f"Hello, my name is {self.name}")

person1 = Person("alice")
person1.say_hello()

 

 

클래스 메서드(Class Method)

  • 첫 번째 매개변수로 cls를 받음 (self 대신 cls 키워드 사용)
  • 클래스 자체를 인자로 받으며, 클래스 변수를 다루거나 새로운 인스턴스를 생성하는 메서드 등을 정의하는데 유용
  • 데코레이터 @classmethod를 사용
class Person:
    count = 0

    def __init__(self, name):
        self.name = name
        Person.count += 1

    @classmethod
    def how_many(cls):
        print(f"지금까지 {cls.count}명이 만들어졌습니다.")

p1 = Person("Alice")
p2 = Person("Bob")
Person.how_many()  # "지금까지 2명이 만들어졌습니다."

 

 

정적 메서드(Static Method)

  • 첫 번째 매개변수로 self나 cls를 받지 않음
  • 주로 클래스나 인스턴스 변수에 접근할 필요가 없는 경우 사용
  • 데코레이터 @staticmethod를 사용
class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b

result = MathUtils.add(3, 5)
print(result)  # 8

 

 

 

🔰 특징                                                                                                                

캡슐화

파이썬은 언어 차원에서 public, private 접근 제어자를 명시적으로 제공하지 않지만, 관례적으로 변수나 메서드 앞에 언더스코어(_)를 붙여 내부적으로 사용함을 암시

  • _변수명: 해당 변수는 내부적 용도로 사용되는 것을 암시 (개발자간 약속)
  • __변수명: 이름 장식(name mangling)을 통해 외부에서 접근하기 어렵게 만듦
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance  # 이 변수는 직접 접근하기 어렵게 함

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if self.__balance >= amount:
            self.__balance -= amount
        else:
            print("잔고 부족")

    def get_balance(self):
        return self.__balance

acc = BankAccount("Alice", 1000)
print(acc.get_balance())  # 1000
acc.deposit(500)
print(acc.get_balance())  # 1500
acc.withdraw(2000)        # "잔고 부족"
print(acc.get_balance())  # 1500

# acc.__balance 는 직접 접근 불가능 (AttributeError 발생)

이와 같이 캡슐화를 통해 내부 데이터는 메서드로만 접근 가능하게 하여 데이터 무결성을 보장

 

 

상속 (Inheritance)

기존 클래스를 재사용하여 새로운 클래스를 만들 수 있게 하는 기능

상속을 통해 기존 클래스(부모 클래스 또는 슈퍼 클래스)의 속성과 메서드를 자식 클래스(서브 클래스)에서 물려받을 수 있고, 필요하다면 확장 또는 수정할 수 있음

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print("동물이 소리를 냅니다.")

class Dog(Animal):  # Animal 클래스를 상속
    def speak(self):
        print(f"{self.name}가 멍멍 짖습니다.")

dog = Dog("멍멍이")
dog.speak()  # "멍멍이가 멍멍 짖습니다."

Dog 클래스는 Animal 클래스를 상속하여 name 속성과 speak() 메서드를 물려받음

그리고 Dog 클래스에서 speak() 메서드를 재정의(오버라이딩)하여 특정한 동작을 구현할 수 있음

 

 

다형성 (Polymorphism)오버라이딩 (Overriding)

  • 다형성(Polymorphism) : 같은 메서드 이름이 다양한 클래스에서 다른 형태로 동작할 수 있음
  • 오버라이딩(Overriding) : 자식 클래스에서 부모 클래스의 메서드를 재정의
class Animal:
    def speak(self):
        print("동물 소리")

class Cat(Animal):
    def speak(self):
        print("야옹")

class Dog(Animal):
    def speak(self):
        print("멍멍")

animals = [Cat(), Dog(), Animal()]
for a in animals:
    a.speak()
    # Cat 인스턴스 -> "야옹"
    # Dog 인스턴스 -> "멍멍"
    # Animal 인스턴스 -> "동물 소리"

같은 speak() 메서드를 호출하지만, 객체의 타입에 따라 다른 결과를 얻는 것을 볼 수 있음



 


🔰
                                                                                                                       

실제 예제: 학생 관리 시스템

class Student:
    def __init__(self, name, student_id):
        self.name = name
        self.student_id = student_id
        self.scores = []

    def add_score(self, score):
        self.scores.append(score)

    def get_average(self):
        if not self.scores:
            return 0
        return sum(self.scores) / len(self.scores)

    def introduce(self):
        print(f"이름: {self.name}, 학번: {self.student_id}, 평균점수: {self.get_average()}")

# 사용 예시
s1 = Student("Alice", "20230001")
s1.add_score(90)
s1.add_score(85)
s1.add_score(100)

s2 = Student("Bob", "20230002")
s2.add_score(70)
s2.add_score(75)

s1.introduce()  # 이름: Alice, 학번: 20230001, 평균점수: 91.666...
s2.introduce()  # 이름: Bob, 학번: 20230002, 평균점수: 72.5

 

정리

  • 클래스는 객체(인스턴스)를 만들기 위한 설계도이며, OOP의 핵심 개념
  • 인스턴스 변수, 클래스 변수를 통해 데이터를 관리하고, 인스턴스 메서드, 클래스 메서드, 정적 메서드를 통해 기능을 구현
  • __init__ 메서드(생성자)를 통해 인스턴스 생성 시 초기화 과정을 수행할 수 있음
  • 접근 제어(명시적 키워드는 없지만 언더스코어 컨벤션), 캡슐화, 상속, 다형성 등을 통해 코드 재사용성과 유연성을 극대화
  • 클래스를 통해 관련된 데이터와 기능을 논리적 단위로 묶어내면 큰 규모의 프로그램을 더 체계적이고 유지 관리하기 용이

 


🔰 과제                                                                                                  

실습 01

개인과제 - 필수

더보기
# %%
문제 1 : 짝수의 합 구하기

📌 요구 지식: 리스트, 반복문, 조건문
배경:
장바구니 금액 중에서 짝수 금액만 골라 합산해야 하는 간단 로직을 연습합니다.
목표:
정수 리스트에서 짝수만 골라 합계를 반환하는 함수를 작성하세요.

데이터:
# 예시 데이터
numbers = [3000, 7000, 2000, 8000, 5000, 10000, 11000]

def sum_even(numbers):
    """
    numbers: 정수 리스트
    반환값: 짝수들의 합 (int)
    """
    # 여기에 코드를 작성하세요
    return

# 예시 실행 (제출 시 주석 처리)
# print(sum_even(numbers))  # 46000

# %%
# numbers = [3000, 7000, 2000, 8000, 5000, 10000, 11000]

def sum_even(numbers):
    total = 0
    for i in numbers:
        if i % 2 == 0:
            total += i
    return total

# print(sum_even(numbers))  # 46000

# %%
문제 2 : 문장 속 단어 개수 세기

📌 요구 지식: 문자열, split(), 반복문(선택)
배경:
리포트에서 단어 수를 집계해야 할 때 기본적으로 공백 기준으로 단어를 셉니다. 연속된 공백도 잘 처리되어야 합니다.
목표:
문자열에서 공백 기준 단어 수를 반환하는 함수를 작성하세요.


데이터:
# 예시 데이터
text = "  hello   world  python  "

def count_words(text):
    """
    text: 문자열
    반환값: 단어 개수 (int)
    """
    # 여기에 코드를 작성하세요
    return

# 예시 실행 (제출 시 주석 처리)
# print(count_words(text))  # 3

# %%
# text = "  hello   world  python  "

def count_words(text):
    blank = text.split()
    word_cnt = len(blank)
    return word_cnt

# print(count_words(text))      # 3

# %%
문제 3 : 문자열 속 숫자 합 구하기

📌 요구 지식: 문자열, 반복문, 조건문
배경:
로그나 코드 문자열 속에 섞여 있는 숫자 문자(0-9) 들만 뽑아 간단히 합계를 낼 때가 있습니다.

목표:
문자열에서 숫자 문자(0-9)만 찾아 모두 더한 합을 반환하세요.
(연속된 숫자라도 각 자리수를 더합니다. 예: "23" → 2+3=5)
데이터:
# ex1. 반환 값 -> 10
text = "a1b23c004"

# ex2. 반환 값 -> 0
text = "Hello"

# ex3. 반환 값 -> 18
text = "9-9=0"

def sum_of_digits(text):
    """
    문자열에서 숫자 문자(0-9)만 찾아 모두 더해 반환.
    예) "a1b23c004" -> 1+2+3+0+0+4 = 10
    """
    # 여기에 코드를 작성하세요
    return

# 예시 실행 (제출 시 주석 처리)
# print(sum_of_digits(text))  # 10

# %%
# text1 = "a1b23c004"
# text2 = "Hello"
# text3 = "9-9=0"

def sum_of_digits(text):

    num = str([0,1,2,3,4,5,6,7,8,9])
    blank = []

    for i in text:
        if i in num:
            blank += i

    chan = list(map(int, blank))

    sumnum = 0
    for i in chan:
        sumnum += i

    return  sumnum

# print(sum_of_digits(text1))
# print(sum_of_digits(text2))
# print(sum_of_digits(text3))

# %%
문제 4 : 합격/불합격 집계하기


📌 요구 지식: 
배경:
학생 점수 데이터에서 기준 점수 이상은 합격, 미만은 불합격으로 분류합니다.
목표:
{이름: 점수} 딕셔너리를 받아 합격/불합격 인원 수를 딕셔너리로 반환하세요. (기본 합격 기준 60점)

데이터:
scores = {"Alice": 85, "Bob": 52, "Chris": 60, "Dana": 59}

def grade_pass_fail(scores, threshold=60):
    """
    반환값 예: {'pass': 2, 'fail': 2}
    """
    # 여기에 코드를 작성하세요
    return

# 예시 실행 (제출 시 주석 처리)
# print(grade_pass_fail(scores))  # {'pass': 2, 'fail': 2}

# %%
# scores = {"Alice": 85, "Bob": 52, "Chris": 60, "Dana": 59}

def grade_pass_fail(scores, threshold=60):
    new_score = {i: ("pass" if j >= 60 else "fail")for i, j in scores.items()}
    new_list = list(new_score.values())
    val1 = new_list.count("pass")
    val2 = new_list.count("fail")
    new_dict = {"pass" : val1, "fail" : val2}
    return new_dict


# print(grade_pass_fail(scores))

# %%
문제 5 : 입고 반영한 재고 합치기


📌 요구 지식: 
배경:
현재 재고와 입고 수량을 합쳐 최신 재고를 만듭니다.

목표:
current와 arrival 두 딕셔너리를 받아 상품별 총 재고 딕셔너리를 반환하세요.

데이터:
current = {"apple": 5, "banana": 2, "milk": 1}
arrival = {"banana": 3, "bread": 4, "milk": 2}

def merge_inventory(current, arrival):
    """
    반환값 예: {'apple': 5, 'banana': 5, 'milk': 3, 'bread': 4}
    """
    # 여기에 코드를 작성하세요
    return

# 예시 실행 (제출 시 주석 처리)
# print(merge_inventory(current, arrival)) 
# {'apple': 5, 'banana': 5, 'milk': 3, 'bread': 4}

# %%
# current = {"apple": 5, "banana": 2, "milk": 1}
# arrival = {"banana": 3, "bread": 4, "milk": 2}

def merge_inventory(current, arrival):

    merge_dict = {}

    for k, v in current.items():
        merge_dict.setdefault(k, []).append(v)
    for k, v in arrival.items():
        merge_dict.setdefault(k, []).append(v)

    # print(merge_dict)
    # {'apple': [5], 'banana': [2, 3], 'milk': [1, 2], 'bread': [4]}

    for i, j in merge_dict.items():
        merge_dict[i] = sum(j)

    return merge_dict

# print(merge_inventory(current, arrival))
# {'apple': 5, 'banana': 5, 'milk': 3, 'bread': 4}

# %%
문제 6 : 두 수의 합(인덱스 반환)


📌 요구 지식: 딕셔너리, 반복문, 조건문
배경:
목표 합을 만드는 두 원소의 인덱스를 빠르게 찾는 패턴 연습입니다.

목표:
정수 리스트 nums와 정수 target이 주어질 때, 합이 target이 되는 서로 다른 두 인덱스를 (i, j)로 반환하세요. (없으면 (-1, -1))


데이터:
# ex1. 반환 값 (0,1)
nums = [2, 7, 11, 15]
target = 9

# ex2. 반환 값 (-1, -1)
nums = [2, 7, 11, 15]
target = 10

def two_sum(nums, target):
    """
    반환값 예: (0, 1)  # nums[0] + nums[1] = 9
    """
    # 여기에 코드를 작성하세요
    return

# 예시 실행 (제출 시 주석 처리)
# print(two_sum(nums, target))

# %%