파이썬 객체 지향 프로그래밍 기초: 클래스와 인스턴스 활용

객체 지향 프로그래밍의 이해

객체 지향 프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어 개발에서 핵심적인 패러다임 중 하나로, 현실 세계의 개념을 코드로 모델링할 수 있게 해줍니다. 이 방식은 데이터(속성)와 그 데이터를 조작하는 함수(메서드)를 하나의 단위인 클래스(Class)로 묶어 관리합니다. 클래스로부터 생성된 구체적인 인스턴스를 객체(Object)라고 부릅니다.

기본 클래스 정의 및 사용

예를 들어, 강아지를 나타내는 Dog 클래스를 만들어 보겠습니다. 이 클래스는 이름과 나이라는 속성을 가지며, 특정 행동(sit, roll over 등)을 수행할 수 있는 메서드를 포함합니다.

class Dog:
    """강아지 클래스 - 이름과 나이를 가지며 기본 동작을 수행함"""

    def __init__(self, name, age):
        """
        초기화 메서드
        :param name: 강아지 이름
        :param age: 강아지 나이
        """
        self.name = name
        self.age = age

    def sit(self):
        print(f"{self.name}가 앉았습니다.")

    def roll_over(self):
        print(f"{self.name}가 굴렀습니다.")

여기서 __init__()은 특수한 메서드로, 새로운 객체가 생성될 때 자동으로 호출됩니다. 첫 번째 매개변수로 항상 self를 받으며, 이는 현재 인스턴스를 참조하는 역할을 합니다. 이를 통해 각 객체는 자신의 속성에 접근할 수 있습니다.

인스턴스 생성과 속성 접근

클래스를 정의한 후에는 실제 객체를 생성할 수 있습니다.

my_dog = Dog("Willie", 6)
print(f"이름: {my_dog.name}")
print(f"나이: {my_dog.age}")

속성에 접근할 때는 점(.) 표기법을 사용하며, 마찬가지로 메서드도 같은 방식으로 호출합니다.

my_dog.sit()
my_dog.roll_over()

여러 객체 생성

동일한 클래스로부터 여러 객체를 자유롭게 생성할 수 있습니다.

your_dog = Dog("Lucy", 3)
print(f"{your_dog.name}는 {your_dog.age}살입니다.")
your_dog.sit()

속성의 초기값 설정 및 변경 방법

모든 속성을 외부에서 입력받는 것이 아니라, 일부는 기본값으로 설정할 수 있습니다. 예를 들어 자동차의 주행 거리(odometer)는 일반적으로 처음에 0으로 시작합니다.

class Car:
    """자동차 클래스"""

    def __init__(self, manufacturer, model, year):
        self.manufacturer = manufacturer
        self.model = model
        self.year = year
        self.mileage = 0  # 기본값 설정

    def get_full_info(self):
        return f"{self.year} {self.manufacturer} {self.model}".title()

    def show_mileage(self):
        print(f"현재 주행 거리: {self.mileage}km")

속성 값 수정 전략

  1. 직접 수정: 인스턴스 속성에 직접 값을 할당
  2. 메서드를 통한 수정: 전용 메서드로 값을 안전하게 업데이트
  3. 증감 처리: 기존 값에 누적하여 조정
# 1. 직접 수정
my_car = Car("Audi", "A4", 2019)
my_car.mileage = 5000
my_car.show_mileage()

# 2. 메서드를 통한 수정
def update_mileage(self, new_mileage):
    if new_mileage >= self.mileage:
        self.mileage = new_mileage
    else:
        print("주행 거리는 감소할 수 없습니다!")

# 3. 증가량 반영
def add_mileage(self, increment):
    if increment > 0:
        self.mileage += increment

상속을 통한 확장

기존 클래스의 기능을 재사용하면서 새로운 기능을 추가하려면 상속(Inheritance)을 사용합니다. 예를 들어, 전기차는 일반 자동차의 특성을 가지되, 배터리 용량이라는 고유 속성이 추가됩니다.

class ElectricCar(Car):
    """전기차 클래스 - 자동차 클래스를 상속"""

    def __init__(self, brand, model, year):
        super().__init__(brand, model, year)  # 부모 클래스 초기화
        self.battery_capacity = 75  # 전기차 전용 속성

    def describe_battery(self):
        print(f"배터리 용량: {self.battery_capacity}kWh")

    # 메서드 오버라이딩
    def show_fuel_status(self):
        print("전기차는 연료 탱크가 없습니다.")

여기서 super().__init__()은 부모 클래스의 초기화 메서드를 호출하여 중복 코드를 줄입니다. 또한, 필요 시 부모의 메서드를 자식 클래스에서 재정의(오버라이딩)할 수 있습니다.

구성(Composition) 패턴

복잡한 클래스를 하나로 유지하기보다는, 관련 기능을 별도의 클래스로 분리하고 이를 조합하는 것이 더 나은 설계입니다. 예를 들어, 전기차의 배터리 시스템을 독립된 클래스로 분리할 수 있습니다.

class Battery:
    """배터리 시스템 클래스"""

    def __init__(self, capacity=75):
        self.capacity = capacity

    def display_info(self):
        print(f"배터리: {self.capacity}kWh")

    def estimate_range(self):
        if self.capacity == 75:
            range_km = 300
        elif self.capacity == 100:
            range_km = 500
        print(f"충전 완료 시 약 {range_km}km 주행 가능")

이제 ElectricCar 클래스 내에서 Battery 객체를 속성으로 포함시킵니다.

class ElectricCar(Car):
    def __init__(self, brand, model, year):
        super().__init__(brand, model, year)
        self.battery = Battery()  # Battery 인스턴스 포함

# 사용 예시
my_tesla = ElectricCar("Tesla", "Model Y", 2023)
print(my_tesla.get_full_info())
my_tesla.battery.display_info()
my_tesla.battery.estimate_range()

모듈과 클래스 임포트

클래스는 별도의 파일(.py)에 저장하고 필요한 곳에서 불러와 사용할 수 있습니다. 이를 통해 코드의 재사용성과 가독성을 높일 수 있습니다.

# car.py 파일에 Car, ElectricCar, Battery 클래스 정의 후
from car import Car, ElectricCar

my_vehicle = Car("Hyundai", "Sonata", 2022)
my_ev = ElectricCar("Kia", "EV6", 2023)

또한, 전체 모듈을 임포트하거나 이름 충돌을 피하기 위해 별칭(alias)을 사용할 수도 있습니다.

import car as vehicle_module
from car import ElectricCar as EV

코드 스타일 가이드

  • 클래스 이름: 카멜케이스 (예: ElectricCar)
  • 메서드 및 속성 이름: 스네이크 케이스 (예: get_full_info)
  • 문서화: 모든 클래스와 중요한 메서드에는 docstring을 포함
  • 공백: 메서드 사이에는 한 줄, 클래스 사이에는 두 줄의 빈 줄 삽입

표준 라이브러리 활용

Python은 다양한 내장 모듈을 제공합니다. 예를 들어, 난수 생성에는 random 모듈을 사용합니다.

import random

# 지정 범위 내 정수 추출
dice_roll = random.randint(1, 6)

# 리스트에서 무작위 항목 선택
players = ["Alice", "Bob", "Charlie"]
winner = random.choice(players)

이처럼 표준 라이브러리를 적절히 활용하면 반복 작업을 줄이고 개발 효율을 높일 수 있습니다.

태그: OOP Python 클래스 상속 구성 메서드 오버라이딩

6월 1일 21:12에 게시됨