Python - 9. 객체 지향 프로그래밍

2025. 1. 19. 19:21프로그래밍 언어/Python

Python은 객체 지향 프로그래밍(OOP)을 지원하는 강력한 언어입니다. 객체 지향 프로그래밍은 데이터를 객체로 모델링하고, 해당 객체의 상태와 동작을 관리하는 방식입니다. 이번 섹션에서는 OOP의 핵심 개념과 활용법을 다룹니다.


9.1 클래스와 객체

클래스 정의

클래스는 객체의 설계도를 나타냅니다.

class Person:
    """사람 클래스"""
    def __init__(self, name, age):
        self.name = name
        self.age = age

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

객체 생성

클래스를 기반으로 객체를 생성합니다.

person1 = Person("Alice", 30)
person1.greet()  # 안녕하세요, 제 이름은 Alice이고, 나이는 30살입니다.

9.2 생성자와 소멸자

생성자

__init__ 메서드는 객체가 생성될 때 호출됩니다.

class Person:
    def __init__(self, name, age):
        print("객체 생성 중")
        self.name = name
        self.age = age

person = Person("Bob", 25)  # 객체 생성 중

소멸자

__del__ 메서드는 객체가 소멸될 때 호출됩니다.

class Person:
    def __del__(self):
        print("객체가 소멸되었습니다.")

person = Person()
del person  # 객체가 소멸되었습니다.

9.3 메서드와 속성

메서드

클래스 내부에서 정의된 함수로, 객체의 동작을 정의합니다.

class Calculator:
    def add(self, a, b):
        return a + b

calc = Calculator()
print(calc.add(3, 5))  # 8

속성

객체의 상태를 저장하는 변수입니다.

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

person = Person("Charlie")
print(person.name)  # Charlie

속성 데코레이터

@property 데코레이터를 사용하면 메서드를 속성처럼 사용할 수 있습니다.

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("반지름은 음수가 될 수 없습니다.")
        self._radius = value

circle = Circle(5)
print(circle.radius)  # 5
circle.radius = 10
print(circle.radius)  # 10

9.4 상속과 다형성

상속

클래스는 다른 클래스의 속성과 메서드를 상속받아 재사용할 수 있습니다.

class Animal:
    def speak(self):
        print("소리를 냅니다.")

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

animal = Animal()
animal.speak()  # 소리를 냅니다.

dog = Dog()
dog.speak()  # 멍멍!

다중 상속과 MRO(Method Resolution Order)

Python은 다중 상속을 지원하며, MRO를 통해 메서드 탐색 순서를 정의합니다. MRO는 super() 호출에도 영향을 줍니다.

class A:
    def action(self):
        print("A")

class B(A):
    def action(self):
        print("B")

class C(A):
    def action(self):
        print("C")

class D(B, C):
    pass

obj = D()
obj.action()  # B
print(D.mro())  # [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]

super() 사용법

super()는 부모 클래스의 메서드를 호출할 때 사용됩니다.

class Parent:
    def greet(self):
        print("부모 클래스의 인사입니다.")

class Child(Parent):
    def greet(self):
        super().greet()
        print("자식 클래스의 인사입니다.")

child = Child()
child.greet()

실행 결과

부모 클래스의 인사입니다.
자식 클래스의 인사입니다.

9.5 캡슐화와 접근 제어자

캡슐화

캡슐화는 객체의 속성과 메서드를 외부에서 직접 접근하지 못하도록 제한하는 것을 의미합니다.

접근 제어자

  • public: 모든 곳에서 접근 가능 (기본값)
  • protected: 클래스와 서브클래스에서만 접근 가능 (_로 표시)
  • private: 클래스 내부에서만 접근 가능 (__로 표시)
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner  # public
        self._balance = balance  # protected
        self.__pin = "1234"  # private

account = BankAccount("Alice", 1000)
print(account.owner)  # Alice
print(account._balance)  # 1000
# print(account.__pin)  # AttributeError

9.6 정적 메서드와 클래스 메서드

정적 메서드

인스턴스와 클래스와 무관하게 동작하는 메서드입니다.

class Utility:
    @staticmethod
    def add(a, b):
        return a + b

print(Utility.add(3, 5))  # 8

클래스 메서드

클래스를 첫 번째 매개변수로 받으며, 클래스 자체를 조작할 때 사용합니다.

class Counter:
    count = 0

    @classmethod
    def increment(cls):
        cls.count += 1

Counter.increment()
print(Counter.count)  # 1

9.7 컴포지션(Composition) vs 상속

컴포지션은 객체를 다른 객체의 속성으로 포함하여 기능을 확장하는 방법입니다. 상속과 달리 더 유연하며, 강한 결합을 피할 수 있습니다.

상속 사용 예

class Engine:
    def start(self):
        print("엔진이 시작되었습니다.")

class Car(Engine):
    pass

car = Car()
car.start()

컴포지션 사용 예

class Engine:
    def start(self):
        print("엔진이 시작되었습니다.")

class Car:
    def __init__(self):
        self.engine = Engine()

    def start(self):
        self.engine.start()

car = Car()
car.start()

컴포지션 vs 상속의 장단점

  • 상속: 코드 재사용성이 높지만, 강한 결합으로 인해 유연성이 떨어질 수 있음.
  • 컴포지션: 객체 간의 결합이 약해 더 유연한 설계 가능.

9.8 추상 클래스와 인터페이스

추상 클래스

추상 클래스는 인스턴스화할 수 없으며, 반드시 서브클래스에서 구현해야 하는 메서드를 정의합니다.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

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

rect = Rectangle(10, 5)
print(rect.area())  # 50

9.9 디자인 패턴 기초

디자인 패턴은 재사용 가능한 코드를 작성하는 데 도움이 되는 소프트웨어 설계 방법론입니다.

싱글톤 패턴

싱글톤 패턴은 클래스의 인스턴스를 하나만 생성하도록 보장합니다.

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2)  # True

이번 섹션에서는 Python의 객체 지향 프로그래밍(OOP)의 주요 개념과 실용적인 활용법을 살펴보았습니다. 이러한 개념을 잘 활용하면 유지보수성과 재사용성이 높은 프로그램을 작성할 수 있습니다.