파이썬 객체지향
• bamjun
객체지향 프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어 개발에서 널리 사용되는 프로그래밍 패러다임 중 하나입니다. 이 접근법은 데이터와 이 데이터를 처리하는 데 필요한 연산들을 객체로 조직화하는 데 중점을 둡니다. 객체지향 프로그래밍의 핵심 개념은 다음과 같습니다:
-
객체(Object): 객체는 데이터(속성)와 이 데이터를 조작할 수 있는 함수(메서드)를 결합한 것입니다. 프로그래밍에서 객체는 현실 세계의 객체를 모델링한 것으로 볼 수 있으며, 특정 클래스의 인스턴스로 표현됩니다.
-
클래스(Class): 클래스는 객체를 생성하기 위한 템플릿 또는 설계도와 같습니다. 클래스는 객체의 속성(데이터)과 행위(메서드)를 정의합니다.
-
상속(Inheritance): 클래스 간에 코드를 재사용하는 메커니즘입니다. 하나의 클래스(부모 클래스)에서 정의된 속성과 행위를 다른 클래스(자식 클래스)가 상속받을 수 있습니다.
-
캡슐화(Encapsulation): 객체의 데이터(속성)와 메서드를 하나로 묶고, 객체의 실제 구현 내용 일부를 외부에서 접근하지 못하도록 숨기는 것을 말합니다. 이를 통해 인터페이스만을 통해 객체와 상호작용하며, 객체의 세부 구현은 외부에서 알 필요가 없습니다.
-
다형성(Polymorphism): 같은 이름의 메서드가 다른 클래스에서 다른 행위를 할 수 있도록 하는 능력을 의미합니다. 이는 메서드 오버로딩(동일한 메서드 이름에 다른 매개변수를 사용)과 오버라이딩(상속받은 메서드를 재정의)을 통해 구현됩니다.
-
추상화(Abstraction): 복잡한 현실의 객체를 단순화하여 필요한 속성과 행위만을 모델링하는 과정입니다. 추상화를 통해 불필요한 세부 사항을 생략하고 중요한 특징들을 강조할 수 있습니다.
객체지향 프로그래밍은 소프트웨어의 재사용성, 유연성, 유지보수성을 향상시키는 데 유리하며, 복잡한 시스템을 더 쉽게 관리하고 이해할 수 있도록 돕습니다. Java, C++, Python, Ruby 등 많은 현대 프로그래밍 언어들이 객체지향 프로그래밍을 지원합니다.
클래스 상속(Inheritance)은 객체지향 프로그래밍(Object-Oriented Programming, OOP)의 핵심 개념 중 하나입니다. 상속을 통해 한 클래스(자식 클래스)가 다른 클래스(부모 클래스)의 속성(변수)과 메서드(함수)를 이어받을 수 있습니다. 이는 코드의 재사용성을 높이고, 코드의 중복을 줄이는 데 유용합니다.
클래스 상속의 주요 특징과 이점은 다음과 같습니다:
-
코드 재사용: 자식 클래스는 부모 클래스의 모든 속성과 메서드를 상속받아, 중복되는 코드를 다시 작성하지 않아도 됩니다.
-
확장성: 자식 클래스는 부모 클래스의 기능을 확장하거나 수정할 수 있습니다. 이를 통해 기존 클래스의 기능을 유지하면서 새로운 기능을 추가할 수 있습니다.
-
유지보수 용이성: 공통 기능을 부모 클래스에 구현해 두면, 여러 자식 클래스에서 이를 상속받아 사용할 수 있어 유지보수가 용이해집니다.
-
다형성 구현: 상속은 다형성(Polymorphism)의 구현에 중요한 역할을 합니다. 같은 인터페이스나 메서드 호출이 상속받은 여러 클래스에서 다양한 방식으로 동작할 수 있습니다.
클래스 상속의 예시를 Python 코드로 살펴보겠습니다:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Animal 클래스를 상속받은 Dog와 Cat 클래스
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # "Woof!"
print(cat.speak()) # "Meow!"
이 예시에서 Dog
와 Cat
클래스는 Animal
클래스로부터 상속받습니다. Animal
클래스는 speak
메서드를 정의하지만 구체적인 구현은 제공하지 않습니다. 이후 Dog
와 Cat
클래스에서 이 메서드를 오버라이딩(재정의)하여 각각의 동물 소리를 반환하도록 합니다.
self
는 Python에서 클래스의 인스턴스 메서드에서 사용되는 키워드입니다. self
키워드는 클래스의 인스턴스 자체를 참조하며, 클래스 내부에서 인스턴스 변수에 접근하거나 다른 인스턴스 메서드를 호출할 때 사용됩니다.
self
의 주요 특징은 다음과 같습니다:
-
인스턴스 참조:
self
는 클래스의 현재 인스턴스를 참조합니다. 인스턴스 메서드 내에서self
를 사용하면, 해당 메서드가 호출된 인스턴스에 접근할 수 있습니다. -
메서드 정의의 첫 번째 매개변수: Python에서 클래스의 인스턴스 메서드를 정의할 때, 첫 번째 매개변수는 항상
self
로 지정됩니다. 이는 규칙이며 Python의 객체지향 구현의 일부입니다. -
인스턴스 변수 접근:
self
를 사용하여 클래스 내에서 인스턴스 변수에 접근하고, 이 변수들을 설정하거나 수정할 수 있습니다. -
다른 메서드 호출: 클래스 내의 다른 메서드를 호출할 때
self
를 통해 이러한 메서드에 접근할 수 있습니다.
예를 들어, Python 클래스에서 self
를 사용하는 방법을 살펴보겠습니다:
class Person:
def __init__(self, name, age):
self.name = name # 인스턴스 변수 설정
self.age = age
def greet(self):
return "Hello, my name is " + self.name # self를 사용하여 인스턴스 변수에 접근
# Person 클래스의 인스턴스 생성
person = Person("John", 30)
# 인스턴스 메서드 호출
print(person.greet()) # "Hello, my name is John"
이 예제에서 __init__
메서드와 greet
메서드는 self
를 사용하여 현재 인스턴스의 변수에 접근하고, 이 변수들을 설정하거나 사용합니다. person.greet()
호출 시, self
는 person
인스턴스를 참조합니다.
클래스 다중 상속과 관련된 MRO(Method Resolution Order, 메서드 해석 순서)는 Python에서 특히 중요한 개념입니다. 다중 상속은 한 클래스가 둘 이상의 부모 클래스로부터 속성과 메서드를 상속받는 것을 말합니다. 이때 MRO는 클래스가 메서드를 호출할 때, 해당 메서드를 찾는 순서를 정의합니다.
다중 상속에서 MRO의 역할과 특징은 다음과 같습니다:
-
메서드 탐색 순서 결정: MRO는 클래스의 메서드를 호출할 때 Python이 어느 클래스의 메서드를 사용할지 결정하는 순서를 정의합니다.
-
C3 선형화 알고리즘: Python의 MRO는 C3 선형화 알고리즘을 사용하여 복잡한 다중 상속 구조에서도 일관되고 예측 가능한 순서를 제공합니다. 이 알고리즘은 클래스 계층 구조에서 모순을 방지하고, 각 클래스를 한 번씩만 호출하도록 보장합니다.
-
__mro__
속성: Python 클래스에는__mro__
속성이 있어, 해당 클래스의 메서드 해석 순서를 튜플 형태로 보여줍니다. -
부모 클래스 처리: MRO는 다중 상속 시 각 부모 클래스의 메서드를 어떤 순서로 처리할지 결정합니다. 이는 때때로 예상치 못한 결과를 초래할 수 있어, 다중 상속을 신중하게 사용해야 합니다.
Python에서 다중 상속과 MRO 예시:
class Base:
def method(self):
print("Base method")
class Child1(Base):
def method(self):
print("Child1 method")
class Child2(Base):
def method(self):
print("Child2 method")
class GrandChild(Child1, Child2):
pass
# GrandChild의 MRO 확인
print(GrandChild.__mro__)
# (<class '__main__.GrandChild'>, <class '__main__.Child1'>, <class '__main__.Child2'>, <class '__main__.Base'>, <class 'object'>)
gc = GrandChild()
gc.method() # "Child1 method" 출력
이 예제에서 GrandChild
클래스는 Child1
과 Child2
를 상속받습니다. MRO에 따라 GrandChild
인스턴스가 method
를 호출하면 Child1
의 method
가 실행됩니다. MRO는 GrandChild.__mro__
를 통해 확인할 수 있으며, 이는 메서드 호출 순서를 나타냅니다.