본문 바로가기

Data Science/컴퓨터 공학 기본 (Computer Science)

[이론] OOP (Object-Oriented Programming)

📌 OOP(Object-Oriented Programming, 객체 지향 프로그래밍)이란?

Object Oriented Programming의 약자로 객체의 관점에서 프로그래밍 하는것 (java, python etc)

반대 개념으로 절차 지향 프로그래밍(Procedure Programming,PP)인 C언어가 있다.
절차 지향 프로그래밍은 프로세스가 함수 단위로 순서대로 진행되는 것을 말한다.

 

+) 객체지향(OOP)과 절차적 프로그래밍(PP) 비교 : https://st-lab.tistory.com/151

 

📌 객체(Object)란?

- 구성요소 하나하나를 객체라고 한다.

- (일반적인 설명상 간단히 하자면)객체 = 대상

📌 클래스(Class)란?

객체들의 공통적인 속성을 모아 정의 내린것

- 코드 작성의 기본단위 이자 객체들을 찍어내는 템플릿

- 속성(attribute)와 행위(Method)를 정의

 

클래스(Class) = 붕어빵틀

객체(Object) = 틀로 만들어진 붕어빵

📌 OOP의 4가지 특성(추상화, 캡슐화, 상속성, 다형성)

✏️ 추상화(Abstraction)

데이터와 메소드를 클래스 하나로 묶어 메소드로 접근 할 수 있도록 하는 방법이고 목적과 관련이 없는 부분을 제거하여 필요한 부분만을 표현한다.  즉, 공통의 속성이나 기능을 묶어 이름을 붙이는 것

- 닭발, 떡볶이, 복지리, 맥주, 소주 등이 있을 때, 이들을 각각의 객체라 하며 묶으려고 하는 상황에서 음식 혹은 스트레스받을 때 생각나는 단어 등으로 크게 정의 할수 있다.
- 간단 요약 : 토끼, 고양이, 강아지,사자 etc > 동물 or 생물
🍀Tip:
1. 이름을 잘 짓자!
- 이름만 보고도 직관적으로 어디에 쓰이는지 어떻게 사용해야 할지 등을 유추가능해야한다.
2. 문서화 하자!
- 클래스, 메소드, 변수 등에 설명 부여
- class or 함수 선언부 바로 밑에 작성

+) 자세한 설명 링크 : https://wikidocs.net/16075

from abc import *
class 추상클래스명(metaclass=ABCMeta):
'''
클래스 설명
'''
     @abstractmethod
     
        def 추상메소드(self):
        '''
        클래스 설명
        '''
            pass

 

✏️ 상속, 재사용(Inheritance)

- 상위 개념의 특징을 하위 개념이 물려받는 것으로서 절차지향과 차이를 보여주는 항목이다.
- 코드의 중복을 없애기 위해 필요하다.
- 동물이라는 부모 클래스가 있을 때,  부모는  생명체로서 숨을 쉬고, 영양소를 섭취하는 기능을 갖고있는데, 여기서 날개를 단 객체를 생성하고 싶으면 기존에 있는 부모 클래스를 활용하여 날개를 가진  조류라는 객체를 생성한다. 그러면 생명체로서 숨을 쉬고, 영양소를 섭취하는 기능을 갖으면서 날개를 단 동물이 되는것이다.

- 부모기능 : 숨쉬기,먹기 
- 자식기능 : 날개 달림 
=  날개달린 숨쉬기,먹기 가능한 동물  

 

+) 자세한 설명 링크 : https://wikidocs.net/16075

기본 형식

class 부모클래스:
    ...내용...

class 자식클래스(부모클래스):
    ...내용...
class Country: #부모클래스
    """Super Class"""
    name = '국가명'
    population = '인구'
    capital = '수도'

    def show(self):
        print('국가 클래스의 메소드입니다.')
        
class Korea(Country): #자식클래스
    """Sub Class"""
    def __init__(self, name):
        self.name = name
        
    def show_name(self):
        print('국가 이름은 : ', self.name)
        
>>> from inheritance import *
>>> a = Korea('대한민국')
>>> a.show()
국가 클래스의 메소드입니다.
>>> a.show_name()
국가 이름은 :  대한민국
>>> a.capital
'수도'
>>> a.name
'대한민국'

메소드 오버라이딩 (Method overriding)

부모 클래스의 메소드를 자식 클래스에서 재정의 하는 것
class Korea(Country):
    """Sub Class"""
    def __init__(self, name,population, capital):
        self.name = name
        self.population = population
        self.capital = capital

    def show(self):
        print(
            """
            국가의 이름은 {} 입니다.
            국가의 인구는 {} 입니다.
            국가의 수도는 {} 입니다.
            """.format(self.name, self.population, self.capital)
        )
            ... 생략

# 부모 클래스의 show()메소드는 무시되고 자식클래스의 show()메소드가 수행      

>>> from inheritance import *
>>> a = Korea('대한민국', 50000000, '서울')
>>> a.show()

            국가의 이름은 대한민국 입니다.
            국가의 인구는 50000000 입니다.
            국가의 수도는 서울 입니다.

+) 부모 메소드 호출하기

 

부모클래스의 메소드도 수행하고, 자식클래스의 메소드의 내용도 함께 출력하기를 원할 때, super() 라는 키워드를 사용
class Korea(Country):

    ... 생략

    def show(self):
        super().show()
        print(
            """
            국가의 이름은 {} 입니다.
            국가의 인구는 {} 입니다.
            국가의 수도는 {} 입니다.
            """.format(self.name, self.population, self.capital)
        )

    ... 생략
    
>>> from inheritance import *
>>> a = Korea('대한민국', 50000000, '서울')
>>> a.show()
국가 클래스의 메소드입니다.

            국가의 이름은 대한민국 입니다.
            국가의 인구는 50000000 입니다.
            국가의 수도는 서울 입니다.

다중상속(Multiple inheritance)

상속 개수 제한 없음

 

class 부모클래스1:
        ...내용...

class 부모클래스2:
        ...내용...

class 자식클래스(부모클래스1, 부모클래스2):
        ...내용...
class Country:
    """Super Class"""
...생략
class Province:
    Province_list = []
class Korea(Country,Province ):
    """Sub Class"""
... 생략

✏️ 캡슐화(Encapsulation)

불필요한 정보는 숨기고 중요한 정보만을 표현해 프로그램을 간단히만듬는 방식으로 데이터 구조와 데이터를 다루는 방법들을 결합 시켜 묶는 것이다. 그래서 캡슐화를 하면 불필요한 정보를 감출 수 있기 때문에, 정보은닉을 할 수 있다는 특징이 있다

 

- 사용자가 변수와 메소드에 직접적으로 접근하여 실수/고의로 데이터를 변경하는 행위를 미연에 방지하기 위해 필요하다.
- 크게 2가지 방법(Private Variable와 Protected members )이 있다. 
- 변수나 메소드 앞에 _ 두 개를 붙이면 외부에서 접근 불가 ex) _ _count , _ _sum(self, number1, number2)
- 단, 특수메소드(앞뒤로 _ _붙음)는 외부에서 접근 가능
접근제어자 문법 의미
Public name 외부로부터 모든 접근 허용
Protected _name 자기 클래스 내부 혹은 상속받은 자식 클래스에서만 접근 허용
Private __name 자기 클래스 내부의 메서드에서만 접근 허용

+) 자세한 설명 링크 :https://www.fun-coding.org/PL&OOP1-5.html

Private Variables(name mangling)

어떤 한 객체 변수를 다른 한 객체의 메소드를 이용해야지만 변경할 수 있게 하는 방법
class Example:
	def __init__(self, name, number):
		self._name = name
		self._number = number
            
 	def get_name(self):
		return self._name
        
	def set_name(self, name):#name 변경 메소드 선언
		self._name = name #name 값 변경
        
	def get_number(self): #name 값 반환 메소드 선언
		return self._number
        
	def set_number(self, number):
		self._number = number

student = Example("홍길동", 15)
print(Example.get_name())
print(Example.get_number())

Protected Members

클래스 외부에서 접근이 불가하고 해당 클래스 내부 하위 클래스에서만 접근하게끔 설정하는 기법
- prefix로 ‘_‘를 사용하며, Sub Class에서 사용하겠다는 의미
class Example1:
    def __init__(self, name, roll, branch):
    # Roll, Branch 출력 메소드 (Protected)
        self._name = name
        self._roll = roll
        self._branch = branch
        
    def _roll_branch(self):
        # Protected data 출력
        print("Roll: ", self._roll)
        print("Branch: ", self._branch)

 class Example2(Example1):  
 #Example1 상속
    def __init__(self, name, roll, branch):
    # Name, Roll, Branch 출력 메소드
        Example1.__init__(self, name, roll, branch)
        
    def details(self):
        # Example1의 Protected 변수, 메소드
        print("Name: ", self._name)
        self._roll_branch()

>>> info = Example2("홍길동", "학부생", "코딩어려워학과")
>>> info._branch = "컴퓨터과학과"
>>> info.details()

Name: 홍길동
Roll: 학부생
Branch: 컴퓨터과학과

 

✏️ 다형성(polymorphism)

여러가지 형태가 존재한다라는 뜻으로 코딩에서는 형태가 같은데 다른 기능을 하는 것을 의미한다

 

 

- 주로 메소드 오버라이딩(Method overriding)를 사용하여 구현 한다
- 메소드 오버로딩(Method overloading)은 python 에서 정식으로 지원되지 않는다. 구현은 가능하지만 코드가 복잡하고 난해해진다.

+) 자세한 설명 링크 : https://brunch.co.kr/@kd4/4

+) 자세한 설명 링크 : https://www.programiz.com/python-programming/polymorphism

# Polymorphism in Class Methods
class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def info(self):
        print(f"I am a cat. My name is {self.name}. I am {self.age} years old.")

    def make_sound(self):
        print("Meow")

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def info(self):
        print(f"I am a dog. My name is {self.name}. I am {self.age} years old.")

    def make_sound(self):
        print("Bark")


cat1 = Cat("Kitty", 2.5)
dog1 = Dog("Fluffy", 4)

for animal in (cat1, dog1):
    animal.make_sound()
    animal.info()
    animal.make_sound()
    
>>> Meow
>>> I am a cat. My name is Kitty. I am 2.5 years old.
>>> Meow
>>> Bark
>>> I am a dog. My name is Fluffy. I am 4 years old.
>>> Bark
# Method Overriding

from math import pi

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

    def area(self):
        pass

    def fact(self):
        return "I am a two-dimensional shape."

    def __str__(self):
        return self.name


class Square(Shape):
    def __init__(self, length):
        super().__init__("Square")
        self.length = length

    def area(self):
        return self.length**2

    def fact(self):
        return "Squares have each angle equal to 90 degrees."


class Circle(Shape):
    def __init__(self, radius):
        super().__init__("Circle")
        self.radius = radius

    def area(self):
        return pi*self.radius**2


a = Square(4)
b = Circle(7)
print(b)
print(b.fact())
print(a.fact())
print(b.area())

>>> Circle
>>> I am a two-dimensional shape.
>>> Squares have each angle equal to 90 degrees.
>>> 153.93804002589985