3. 객체 지향 프로그래밍
객체 지향 프로그래밍에서는 클래스와 인스턴스 개념을 학습하며, 생성자와 소멸자, 상속과 다형성 등 객체 지향의 기본 원리를 이해합니다. 메서드 오버라이딩과 오버로딩, 프로퍼티와 메서드를 활용하여 객체 지향 프로그래밍의 힘을 체감하게 됩니다.
3.1 클래스와 인스턴스
3.1.1 객체 지향 프로그래밍 기본 개념
- 객체(Object): 프로그램에서 조작할 수 있는 독립적인 개체로, 속성과 동작을 가질 수 있다.
- 클래스(Class): 객체를 생성하는 틀로, 객체의 속성과 동작을 정의한다.
- 인스턴스(Instance): 클래스를 기반으로 생성된 객체. 클래스를 통해 객체를 만들면 인스턴스가 생성된다.
3.1.2 클래스와 인스턴스 생성하기
class Car:
# 클래스 변수
wheels = 4
def __init__(self, make, model):
# 인스턴스 변수
self.make = make
self.model = model
# 인스턴스 생성
my_car = Car("Toyota", "Corolla")
print(my_car.wheels) # 4 출력
print(my_car.make) # Toyota 출력
print(my_car.model) # Corolla 출력
3.1.3 클래스와 인스턴스 변수
- 클래스 변수(Class Variable): 클래스에서 선언되고 클래스에 속한 모든 인스턴스가 공유하는 변수. 클래스 레벨에서 선언된다.
- 인스턴스 변수(Instance Variable): 인스턴스에 속하며, 각 인스턴스가 독립적으로 가지고 있는 변수. 일반적으로 생성자에서 선언된다.
3.1.4 클래스와 인스턴스 메서드
- 클래스 메서드(Class Method): 클래스에 속한 함수로, 클래스 변수를 사용할 수 있다. 첫 번째 인자로 cls를 사용한다.
- 인스턴스 메서드(Instance Method): 인스턴스에 속한 함수로, 인스턴스 변수를 사용할 수 있다. 첫 번째 인자로 self를 사용한다.
class Car:
wheels = 4
def __init__(self, make, model):
self.make = make
self.model = model
def honk(self): # 인스턴스 메서드
print(f"{self.make} {self.model} is honking.")
@classmethod
def wheel_count(cls): # 클래스 메서드
print(f"All cars have {cls.wheels} wheels.")
my_car = Car("Toyota", "Corolla")
my_car.honk() # Toyota Corolla is honking. 출력
Car.wheel_count() # All cars have 4 wheels. 출력
3.1.5 클래스와 인스턴스 속성
- 클래스 속성(Class Attribute): 클래스 변수와 클래스 메서드에 해당한다.
- 인스턴스 속성(Instance Attribute): 인스턴스 변수와 인스턴스 메서드에 해당한다.
3.1.6 인스턴스의 문자열 표현과 특수 메서드
- str: 인스턴스를 문자열로 표현하는 메서드이며, print() 함수에서 호출된다.
- repr: 인스턴스를 개발자에게 보여줄 문자열로 표현하는 메서드이며, 대화형 인터프리터에서 호출된다.
class Car:
def __init__(self, make, model):
self.make = make
self.model = model
def __str__(self):
return f"{self.make} {self.model}"
def __repr__(self):
return f"Car('{self.make}', '{self.model}')"
my_car = Car("Toyota", "Corolla")
print(my_car) # Toyota Corolla 출력
print(repr(my_car)) # Car('Toyota', 'Corolla') 출력
3.2 생성자와 소멸자
3.2.1 생성자와 소멸자 개념
- 생성자(Constructor): 인스턴스가 생성될 때 자동으로 호출되는 메서드로, 인스턴스 변수를 초기화한다. 주로 init 메서드를 사용한다.
- 소멸자(Destructor): 인스턴스가 소멸될 때 자동으로 호출되는 메서드로, 인스턴스의 정리 작업을 수행한다. 주로 del 메서드를 사용한다.
3.2.2 init() 생성자와 del() 소멸자 메서드
class Car:
def __init__(self, make, model):
self.make = make
self.model = model
print(f"{self.make} {self.model} created.")
def __del__(self):
print(f"{self.make} {self.model} destroyed.")
my_car = Car("Toyota", "Corolla") # Toyota Corolla created. 출력
del my_car # Toyota Corolla destroyed. 출력
3.2.3 상위 클래스의 생성자 호출
상위 클래스의 생성자를 호출하려면 super() 함수를 사용하거나, 클래스 이름을 사용하여 직접 호출할 수 있다.
class Vehicle:
def __init__(self):
print("Vehicle created.")
class Car(Vehicle):
def __init__(self, make, model):
super().__init__() # 상위 클래스 생성자 호출
self.make = make
self.model = model
my_car = Car("Toyota", "Corolla") # Vehicle created. 출력
3.2.4 객체 소멸과 참조 카운트
- 객체 소멸: 파이썬에서는 가비지 컬렉터(Garbage Collector)가 메모리 관리를 담당하며, 더 이상 참조되지 않는 객체는 자동으로 소멸한다.
- 참조 카운트(Reference Count): 객체에 대한 참조 횟수를 의미하며, 참조 카운트가 0이 되면 객체는 소멸한다.
import sys
class MyClass:
pass
my_object = MyClass()
print(sys.getrefcount(my_object)) # my_object의 참조 카운트 출력
another_reference = my_object
print(sys.getrefcount(my_object)) # 참조 카운트가 증가함
another_reference = None
print(sys.getrefcount(my_object)) # 참조 카운트가 감소함
3.2.5 컨텍스트 매니저와 with 문
- 컨텍스트 매니저: __enter__와 exit 메서드를 구현한 객체로, with 문을 사용해 리소스를 획득 및 해제하는 작업을 담당한다.
- with 문: 컨텍스트 매니저를 사용하여 리소스를 안전하게 관리할 수 있는 구문이다.
class MyResource:
def __enter__(self):
print("Resource acquired.")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Resource released.")
with MyResource() as resource:
print("Resource in use.")
3.2.6 소멸자와 try-except-finally 구문
- 소멸자와 try-except-finally 구문을 사용하면 객체 소멸 과정에서 발생할 수 있는 예외를 안전하게 처리할 수 있다.
class MyResource:
def __init__(self):
self.resource = "Resource acquired."
def __del__(self):
try:
print("Resource released.")
self.resource = None
except Exception as e:
print(f"Exception during release: {e}")
finally:
print("Resource cleanup complete.")
resource = MyResource()
del resource
3.2.7 클래스와 인스턴스의 복사
파이썬에서 객체를 복사할 때는 copy 모듈의 copy() 함수(얕은 복사) 또는 deepcopy() 함수(깊은 복사)를 사용한다.
import copy
class MyClass:
def __init__(self, value):
self.value = value
original = MyClass(42)
shallow_copy = copy.copy(original)
deep_copy = copy.deepcopy(original)
print(original.value) # 42 출력
print(shallow_copy.value) # 42 출력
print(deep_copy.value) # 42 출력
3.3 상속과 다형성
3.3.1 상속 개념과 활용 방법
- 상속: 기존 클래스의 속성과 메서드를 물려받아 새로운 클래스를 정의하는 것으로, 코드 재사용 및 확장성을 증가시킨다.
- 기본 사용법: 새로운 클래스 선언 시 괄호 안에 기존 클래스를 명시한다.
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("Woof!")
dog = Dog()
dog.speak() # Woof! 출력
3.3.2 상속과 메서드 오버라이딩
- 메서드 오버라이딩: 자식 클래스에서 부모 클래스의 메서드를 재정의하는 것으로, 동일한 메서드 이름을 사용하면 자식 클래스의 메서드가 우선한다.
class Animal:
def speak(self):
print("Animal speaks")
class Dog(Animal):
def speak(self):
print("Woof!")
dog = Dog()
dog.speak() # Woof! 출력 (오버라이딩 된 메서드 호출)
3.3.3 다형성 개념과 활용 방법
- 다형성: 동일한 인터페이스를 사용하여 다양한 객체 타입에 대한 작업을 수행할 수 있는 기능으로, 상속과 메서드 오버라이딩을 통해 구현된다.
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
def make_speak(animal):
animal.speak()
dog = Dog()
cat = Cat()
make_speak(dog) # Woof! 출력
make_speak(cat) # Meow! 출력
3.3.4 추상 클래스와 추상 메서드
- 추상 클래스: 인스턴스화할 수 없는 클래스로, 자식 클래스에서 구현해야 하는 메서드(추상 메서드)를 강제하는 역할을 한다.
- 추상 메서드: 선언만 있고 구현이 없는 메서드로, 자식 클래스에서 반드시 오버라이딩해야 한다.
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("Woof!")
# animal = Animal() # TypeError 발생 (추상 클래스 인스턴스화 불가)
dog = Dog()
dog.speak() # Woof! 출력
3.3.5 인터페이스와 믹스인
- 인터페이스: 객체의 외부에서 사용할 수 있는 메서드 및 속성의 집합으로, 클래스간의 협력 방식을 명시한다.
- 믹스인: 여러 클래스에서 재사용할 수 있는 메서드 집합으로, 다중 상속을 통해 기능을 추가할 때 사용된다.
class FlyMixin:
def fly(self):
print("I can fly!")
class SwimMixin:
def swim(self):
print("I can swim!")
class Animal:
def speak(self):
pass
class Bird(Animal, FlyMixin):
pass
class Fish(Animal, SwimMixin):
pass
bird = Bird()
fish = Fish()
bird.fly() # I can fly! 출력
fish.swim() # I can swim! 출력
3.3.6 다중 상속과 메서드 해결 순서(Method Resolution Order, MRO)
- 다중 상속: 여러 클래스를 동시에 상속받는 것으로, 복잡한 상속 구조가 될 수 있다.
- MRO: 다중 상속 시 메서드 호출 순서를 결정하는 알고리즘이다. 파이썬에서는 C3 선형화 알고리즘을 사용한다.
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.mro()) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
3.3.7 super() 함수를 이용한 다중 상속 문제 해결
- super() 함수: MRO에 따라 부모 클래스의 메서드를 호출할 수 있는 함수로, 다중 상속 문제를 해결하는 데 사용된다.
class A:
def greeting(self):
print("Hello from A")
class B(A):
def greeting(self):
print("Hello from B")
super().greeting()
class C(A):
def greeting(self):
print("Hello from C")
super().greeting()
class D(B, C):
def greeting(self):
print("Hello from D")
super().greeting()
d = D()
d.greeting()
# Hello from D
# Hello from B
# Hello from C
# Hello from A 출력
3.3.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
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
rectangle = Rectangle(5, 10)
circle = Circle(7)
print(rectangle.area()) # 50 출력
print(circle.area()) # 153.86 출력
3.4 메소드 오버라이딩과 오버로딩
3.4.1 메서드 오버라이딩 개념과 활용 방법
- 메서드 오버라이딩: 자식 클래스에서 부모 클래스의 메서드를 재정의하는 것으로, 동일한 메서드 이름을 사용하면 자식 클래스의 메서드가 우선적으로 호출된다.
class Animal:
def speak(self):
print("Animal speaks")
class Dog(Animal):
def speak(self):
print("Woof!")
dog = Dog()
dog.speak() # Woof! 출력 (오버라이딩 된 메서드 호출)
3.4.2 메서드 오버로딩 개념과 활용 방법
- 메서드 오버로딩: 동일한 메서드 이름을 가지지만, 매개변수의 수나 타입이 다른 여러 메서드를 동시에 정의하는 것으로, Java와 같은 언어에서 지원된다.
- 파이썬에서는 메서드 오버로딩을 직접 지원하지 않지만, 기본 인수 값이나 가변 인수를 사용해 비슷한 효과를 낼 수 있다.
class Calculator:
def add(self, a, b, c=0):
return a + b + c
calc = Calculator()
print(calc.add(1, 2)) # 3 출력
print(calc.add(1, 2, 3)) # 6 출력
3.4.3 파이썬에서는 메서드 오버로딩이 지원되지 않는 이유
- 파이썬에서는 메서드 오버로딩을 지원하지 않는 이유는 여러 가지가 있지만, 주된 이유는 파이썬이 동적 타입 언어이기 때문이다.
- 동적 타입 언어에서는 메서드를 호출할 때, 인수의 타입을 미리 알 수 없으므로, 메서드 오버로딩이 필요하지 않다.
3.4.4 연산자 오버로딩을 이용한 클래스 활용
연산자 오버로딩: 클래스에서 정의된 연산자를 재정의하여, 인스턴스끼리의 연산을 가능하게 하는 것이다.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # (4, 6) 출력
3.4.5 slots 속성을 이용한 객체 메모리 최적화
__slots__: 객체의 동적 속성 추가를 제한하여 메모리를 절약할 수 있는 속성이다. 클래스 정의 시 __slots__에 허용된 속성만 객체에 추가할 수 있다. 이를 사용하면 객체가 메모리를 더 효율적으로 사용할 수 있다.
class MyClass:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
obj = MyClass(1, 2)
obj.x = 3
obj.y = 4
# obj.z = 5 # AttributeError 발생: 'MyClass' object has no attribute 'z'
3.5 프로퍼티와 메서드
3.5.1 프로퍼티 개념과 활용 방법
3.5.1.1 프로퍼티란?
- 프로퍼티(Property): 클래스 내부의 메서드를 속성처럼 사용할 수 있게 하는 데코레이터이다. 값을 가져오거나 변경하는 작업을 메서드 호출 없이 수행할 수 있다.
3.5.1.2 프로퍼티의 활용 예시
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("Radius cannot be negative")
self._radius = value
circle = Circle(5)
print(circle.radius) # 5 출력
circle.radius = 10
print(circle.radius) # 10 출력
3.5.2 게터(Getter)와 세터(Setter) 메서드
3.5.2.1 게터(Getter)와 세터(Setter)란?
- 게터(Getter): 클래스의 속성을 읽어오는 메서드
- 세터(Setter): 클래스의 속성을 변경하는 메서드
3.5.2.2 게터와 세터의 활용 예시
class Person:
def __init__(self, first_name, last_name):
self._first_name = first_name
self._last_name = last_name
def get_full_name(self):
return f"{self._first_name} {self._last_name}"
def set_full_name(self, full_name):
parts = full_name.split()
self._first_name = parts[0]
self._last_name = parts[1]
person = Person("John", "Doe")
print(person.get_full_name()) # John Doe 출력
person.set_full_name("Jane Smith")
print(person.get_full_name()) # Jane Smith 출력
3.5.3 프로퍼티를 이용한 읽기 전용 클래스 속성
3.5.3.1 읽기 전용 클래스 속성이란?
- 읽기 전용 클래스 속성: 클래스의 속성을 읽을 수만 있고 변경할 수 없는 속성으로, 프로퍼티 데코레이터를 사용해 구현한다.
3.5.3.2 읽기 전용 클래스 속성의 활용
class ReadOnlyCircle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
circle = ReadOnlyCircle(5)
print(circle.radius) # 5 출력
# circle.radius = 10 # AttributeError 발생: can't set attribute
3.5.4 메소드 타입과 staticmethod, classmethod, 인스턴스 메서드
3.5.4.1 메소드 타입 개념
- 인스턴스 메서드: 객체의 인스턴스를 사용하여 호출되는 일반적인 메서드로, 첫 번째 인수로 self를 받는다.
- 클래스 메서드: 클래스 전체에 영향을 주는 메서드로, 첫 번째 인수로 cls를 받는다.
- 정적 메서드: 클래스와 인스턴스 모두에서 호출할 수 있는 메서드로, 클래스와 인스턴스 정보에 접근할 수 없다.
3.5.4.2 staticmethod, classmethod, 인스턴스 메서드의 개념과 차이점
- @staticmethod: 정적 메서드 데코레이터로, 클래스나 인스턴스와 연관이 없는 독립적인 메서드를 정의할 때 사용한다.
- @classmethod: 클래스 메서드 데코레이터로, 클래스 전체에 영향을 주는 메서드를 정의할 때 사용한다.
- 인스턴스 메서드: 일반적인 메서드로, 객체의 인스턴스를 사용하여 호출한다.
3.5.4.3 staticmethod, classmethod, 인스턴스 메서드의 활용 예시
class MyClass:
class_var = 0
def __init__(self):
self.instance_var = 1
def instance_method(self):
print("This is an instance method")
@classmethod
def class_method(cls):
print("This is a class method")
@staticmethod
def static_method():
print("This is a static method")
obj = MyClass()
obj.instance_method() # This is an instance method 출력
MyClass.class_method() # This is a class method 출력
MyClass.static_method() # This is a static method 출력
3.5.5 메서드 체인과 체이닝
3.5.5.1 메서드 체인과 체이닝이란?
- 메서드 체인(Method Chaining): 메서드를 연속적으로 호출하는 것으로, 각 메서드가 호출된 후에 self를 반환하게 하여, 다음 메서드를 호출할 수 있게 한다.
3.5.5.2 메서드 체인과 체이닝의 활용 예시
class Chain:
def __init__(self, data):
self.data = data
def add(self, value):
self.data += value
return self
def multiply(self, value):
self.data *= value
return self
chain = Chain(1)
result = chain.add(2).multiply(3).add(4).data
print(result) # 13 출력
3.5.6 메서드 데코레이터를 이용한 메서드 활용
3.5.6.1 메서드 데코레이터 개념
- 메서드 데코레이터: 메서드에 기능을 추가하거나 변경하기 위해 사용하는 함수로, 다른 함수를 인수로 받아 새로운 함수를 반환한다.
3.5.6.2 @classmethod, @staticmethod, @property, @abstractmethod 데코레이터의 활용 예시
- @classmethod
class MyClass:
class_var = "Hello, World!"
@classmethod
def print_class_var(cls):
print(cls.class_var)
MyClass.print_class_var() # Hello, World! 출력
- @staticmethod
class MyClass:
@staticmethod
def add(a, b):
return a + b
result = MyClass.add(1, 2)
print(result) # 3 출력
- @property
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
circle = Circle(5)
print(circle.radius) # 5 출력
- @abstractmethod
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("Woof!")
dog = Dog()
dog.speak() # Woof! 출력
2023.05.07 - [프로그래밍/파이썬(Python) 기초부터 ~] - [파이썬(PYTHON) : 중급] 함수 고급 활용
'GD's IT Lectures : 기초부터 시리즈 > 파이썬(Python) 기초부터 ~' 카테고리의 다른 글
[파이썬(PYTHON) : 중급] 파일과 디렉토리 처리 (0) | 2023.05.10 |
---|---|
[파이썬(PYTHON) : 중급] 모듈과 패키지 고급 활용 (2) | 2023.05.09 |
[파이썬(PYTHON) : 중급] 함수 고급 활용 (1) | 2023.05.07 |
[파이썬(PYTHON) : 중급] 고급 문자열 처리 (1) | 2023.05.07 |
[파이썬(PYTHON)] 기본적인 자료구조 - 집합 (0) | 2023.05.03 |
댓글