Programing Language/Python

파이썬의 객체지향 프로그래밍 4가지 특성과 객체 메모리 효율적으로 다루기

JHeaon 2023. 6. 25. 19:05

객체지향 프로그래밍

객체지향 프로그래밍 (Object Oriented Programming)이란 데이터를 추상화시켜 상태와 행위를 가진 객체로 만들고 그 객체들 간의 유기적인 상호작용을 통해 흐름을 구성하는 프로그래밍 방법을 의미한다. 

  • Class : 설계도
  • instance : 설계도에 찍혀 나온 실체, 하나의 class로 만들어진 instance는 각각 독립적이다. 

📢 프로그램을 실제 세상에 가깝게 모델링하는 기법이다. 

 

 

 

객체지향 프로그래밍의 4가지 특성

객체지향의 프로그래밍의 4가지 원칙은 다음과 같다. 지금부터는 해당 내용을 하나씩 코드를 들어가면서 살펴보고 마지막으로 컴포지션에 대해서 알아보도록 하자.

 

  • 추상화 (abstraction) : 불 필요한 것은 숨기고 중요한 정보만을 표현 함으로써 공통의 속성 값이나 행위를 하나로 묶어 이름을 붙이는 것을 의미 
  • 캡슐화 (encapsulation) : 서로 연관있는 속성과 기능들을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것을 의미
  • 상속 (inheritance) : 부모 클래스 기능을 재활용하여 새로운 자식 클래스를 만드는 것을 의미한다. 
  • 다형성 (Polymoriphism) : 객체를 부품화 하여 다른 여러 형태를 가질 수 있도록 한다. 

 

 

 

 

 

1. 추상화

불필요한 정보는 숨기고, 필요한 정보만 표현해서 공통의 속성 값이나 행위를 하나로 묶어 이름을 붙이는 것을 말한다.

예를 들어서 로봇이 하나 있고, 그 로봇의 이름은 siri, 제조번호는 0001이며 안녕하세요 라는 행위를 할 수 있다고 가정하고, 이를 추상화하여 Python 코드로 나타낸다면 다음과 같이 나타 낼 수 있다. 

 

🖥️ code.py

class Robot:
    """
    Robot을 추상화하여 클래스로 나타낸 코드 입니다. 
    """
    
    def __init__(self, name, code):
    	self.name = name
        self.code = code
    
    def say_hello(self) -> str:
        return "안녕하세요"

 

 

 

 

 

 

 

2. 캡슐화

캡슐화란 서로 연관있는 속성과 기능들을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것을 의미한다. 다른 언어에서는 캡슐화를 지키기 위해서 접근 제어자라는 것을 사용하는데, 파이썬은 일반적으로 다른 언어에 비해 다이다믹 한 언어이기도 하고, 접근 제어자가 따로 존재하지 않는다.

 

따라서 명시적으로 변수명이나 함수명을 지을 때 표현하기도 하는데 자바의 접근제어자와 비교해 보면 다음과 같이 비슷하게 사용됨을 느낄 수 있다. 왼쪽에는 자바에서 사용하는 접근 제어자를 의미하고 오른쪽은 파이썬에서 접근 제어자를 표현하기 위한 하나의 개발자들끼리의 약속의 형태를 나타내고 있다. 

자바 파이썬
public 변수명, 함수명
protect _변수명, _함수명
private __변수명, __함수명

📢 자바와 파이썬에서 사용되는 접근제어자 형태가 서로 완벽하게 같은 기능을 하는 것이 아닌 각 역활이 이런 역활을 하는 것 같다라는 필자의 개인적인 의견임을 알린다.

 

접근제어자인 private는 클래스 내에서만 사용가능하기 때문에, 외부에서 값을 사용하려면 일반적으로 getter, setter 메서드를 사용하여 값을 읽거나 변경할 수 있다. 파이썬에서는 이를 @property을 이용하여 처리한다.

 

🖥️ code.py

class Person:
    def __init__(self):
        self.__age = 0
 
    @property
    def age(self):           # getter
        return self.__age
    
    @age.setter
    def age(self, value):    # setter
        
        # 해당 처럼 조건을 걸어서 validate 처리를 할 수 있다. 
        # if value < 0:
        #     raise ValueError
        
        self.__age = value
 
james = Person()
james.age = 20
print(james.age)

 

 

 

 

 

 

3. 상속

부모 클래스가 갖는 모든 메서드와 속성이 자식 클래스에게 그대로 상속되는 것을 의미한다. 

 

예를 들어서 사람이라는 클래스가 있고, 그 아래에 학생이나, 교수 등의 자식 클래스가 있다면 이를 상속을 통해 표현할 수 있다. 

 

🖥️ code.py

class Person:

    def __init__(self, name: str ,phone_number: int):
        self.name: str = name
        self.phone_number: int = phone_number
        

class Student(Person):
    
    def study(self) -> str:
        return "공부를 합니다."
    
    
class Professer(Person):
	
    def research(self) -> str:
        return "연구를 합니다."

 

만약에 부모 클래스에 있는 함수의 이름과 같은 함수를 자식 클래스에서 재정의를 하고, 자식 클래스에서 해당 함수를 불러오면 자식 클래스에 있는 함수가 불러오게 되는데 이를 메서드 오버라이딩 이라고 한다. 

 

📢 메서드 오버라이딩 vs 메서드 오버로딩

메서드 오버라이딩 : 부모 클래스의 메서드 동작방법을 재정의 하여 우선적으로 사용하는 것

매써드 오버로딩 : 이름은 같지만 파라미터의 수에 차이를 둔 다음 메서드를 중복으로 선언하는 것을 의미한다. 

 

🖥️ code.py

class Person:

    def __init__(self, name: str, phone_number: int):
        self.name: str = name
        self.phone_number: int = phone_number
        

class Student(Person):

    def __init__(self, name: str, phone_number: int, student_code: int):
        self.name: str = name
        self.phone_number: int = phone_number
        
    
    def study(self) -> str:
        return "공부를 합니다."
    
    
class Professer(Person):
	
    def research(self) -> str:
        return "연구를 합니다."

 

만약 자식 클래스에서, 부모 메서드에 있는 함수나 변수가 필요한 경우에는 super() 함수를 이용하여 가져온다. 

 

🖥️ code.py

class Person:

    def __init__(self, name: str, phone_number: int):
        self.name: str = name
        self.phone_number: int = phone_number
        

class Student(Person):

    def __init__(self, name: str, phone_number: int, student_code: int):
        super().__init__(name, phone_number)
        self.student_code: int = student_code
        
    
    def study(self) -> str:
        return "공부를 합니다."

 

 

 

 

 

 

 

4. 다형성

어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질을 의미한다. 아까 상속에서 이야기했던 메서드 오버라이딩과 메서드 오버로딩이 그의 예라고 볼 수 있다. 

 

🖥️ code.py // 메서드 오버라이딩 : 부모 클래스의 메서드 동작방법을 재정의 하여 우선적으로 사용하는 것

class Person:

    def __init__(self, name: str, phone_number: int):
        self.name: str = name
        self.phone_number: int = phone_number
        

class Student(Person):

    def __init__(self, name: str, phone_number: int, student_code: int):
        self.name: str = name
        self.phone_number: int = phone_number

 

🖥️ code.py // 메서드 오버로딩 : 이름은 같지만 파라미터의 수에 차이를 둔 다음 메서드를 중복으로 선언하는 것을 의미

class SampleA():
    def add(self, x: int, y: int) -> int:
        return x + y

    def add(self, x: int, y: int, z: int) -> int:
        return x + y + z

 

 

 

 

 

 

객체의 메모리를 효율적으로 관리하는 방법

파이썬에서는 각 객체의 속성을 저장하기 위해  __dict__에 딕셔너리 형태로 관리된다. 하지만 이를 __dict__ 필드에 저장하게 되면 외부에서 새로운 필드(멤버)를 자유롭게 추가할 수 있고, 메모리 사용량이 많아 메모리 문제가 발생할 수 있다는 문제가 있다. 따라서 이를 해결하기 위해서 __slots__이라는 메서드를 통해서 필드 생성에 대한 제약과 메모리 절약을 잡을 수 있다. 

 

🖥️ code.py

"""
__dicts__
"""
class Foo :
    
    def __init__(self, x, y) :
        self.x = x
        self.y = y

foo = Foo(1, 2)
foo.z = 10  # 클래스 외부에서 새로운 필드 z를 추가 가능




"""
__slots__
"""
class Bar :
    
    __slots__ = ['x', 'y']
    
    def __init__(self, x, y) :
        self.x = x
        self.y = y

bar = Bar(1, 2) 
bar.z = 10 # AttributeError: 'Bar' object has no attribute 'z'

 

'Programing Language/Python'의 다른글

  • 현재글 파이썬의 객체지향 프로그래밍 4가지 특성과 객체 메모리 효율적으로 다루기

관련글