본문 바로가기
GD's IT Lectures : 기초부터 시리즈/파이썬(Python) 기초부터 ~

[파이썬(PYTHON) : 중급] 객체 지향 프로그래밍

by GDNGY 2023. 5. 9.

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) : 중급] 함수 고급 활용

 

[파이썬(PYTHON) : 중급] 함수 고급 활용

2. 함수 고급 활용 함수 고급 활용에서는 재귀 함수, 가변 인자와 키워드 인자, 데코레이터, 제너레이터와 같은 고급 함수 사용법을 다룹니다. 이를 통해 코드의 재사용성과 유지 보수성을 높이

gdngy.tistory.com

 

반응형

댓글