OOP(Object Oriented Programming)
oop(객체지향 프로그래밍) 이전에 사용해오던 프로그래밍 방식은 procedural programming(절차지향 프로그래밍)이다.
procedural programming은 코드를 작성할 때, 실행하고자 하는 순서대로 코드를 촤라락 작성하는 방법이다.
반면 oop는 구현하고자 하는 기능들을 따로따로 작성해놓고, 필요할때 object(객체)로 불러와 사용하는 방법이라고 이해했다. 일반적으로 교수들은 붕어빵으로 비유하고, 현업에서는 '실체가 있는 것'을 구현하는 것이라 설명한다고 한다.
oop가 없으면 사람 1, 사람 2, 사람 3 이라는 변수를 설정할 때 마다 변수를 새로 만들어야하고, 만약 [이름, 나이]로만 변수를 생성하다가, 어느 순간 [이름, 나이, 성별] 이렇게 데이터의 형태를 바꾸려고 하면 하나하나 다 바꿔주어야 한다.
oop는 사람 이라는 class(주로 class를 사용하므로) 를 생성해놓고 변수값들을 넣어주면 새로운 객체가 생성된다. 새로운 기능이나 변수를 추가하는 것도 간편하다. A 문제를 해결할 때 사용한 기능이 B 에도 필요하다면 하나의 객체로 불러오기만 하면 된다. 그리고 현업에서 여러 사람이 같은 코드를 수정하는 경우, 어떤 class에서 문제가 발생하면 한 사람이 그 class만 바꿔주면 문제가 해결되는(유지보수 측면의) 편리함도 갖추었다.
oop의 개념이 확장되고 여러 방향으로 응용됨에 따라, 정확히 oop란 무엇이다! 하는 개념은 없어지고 있는 듯 하다. 다만 효율성(캡슐화, 기능의 재사용 가능, 유지보수 쉬움)을 높이기 위해 사용된다는 목적만은 지킨 채, 필요에 따라 기본 oop 개념을 자유롭게 변형해서 사용하면 될 것 같다.
예시)
절차지향 프로그래밍
# 케이스 1 : 함수활용
def Person():
name = 'Jade'
age = 23
gender = 'F'
return name,age,gender
print(Person())
# 케이스 2 : 변수활용
name = 'Jade'
age = 23
gender = 'F'
print(name, age, gender)
객체지향 프로그래밍(OOP)
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def info(self):
return self.name, self.age, self.gender
j = Person('Jade', 23, 'F')
print(j.name) #결과 : 'Jade'
print(j.age) #결과 : 23
print(j.gender) #결과 : 'F'
print(j.info()) #결과 : ('Jade', 23, 'F')
실체가 있는 현실의 개념을 class로 구현하고 나면, 이것을 object화 또는 instance화 할 수 있다. object가 instance보다 좀 더 포괄적 개념이며, class를 이용해 object로 생성하고 나서, object가 메모리할당 된 것을 instance라 부른다.
OOP의 구성
1. 캡슐화
하나의 객체로 구현하고자 하는 기능 및 변수들을 하나로 묶는 것. 접근 제한자를 사용하여 기능 및 변수에 접근을 제한할 수 있다. (기능이 많아질수록 재사용 가능성은 줄어드는 경향이 있다.) 주로 class로 묶어서 사용한다.
#variable : age, name / function : info 포함 한 class
class Person:
def __init__(self, age, name) :
self.age = age
self.name = name
def info(self):
print('Name :', self.name, 'Age :', self.age)
캡슐화의 장점 :
- 모듈화 가능 (함수, 메소드, 클래스 등 활용한 기능의 분리)
- 기능의 분리로 디버깅이 편리
- 기능의 분리로 소스코드의 목적을 알기 쉬움
- under score( _ )를 이용하여 접근 제어 가능 : 외부로부터의 무분별한 접근 금지
2. 상속(inheritance)과 포함(composition)
상속은 상위 클래스를 하위(서브) 클래스로 그대로 '복사'하는 개념이라면, 포함은 상위 클래스의 일부를 차용하여 새로운 클래스를 만드는 것을 말한다.
*컴퓨터의 성능(용량, 속도 등)이 좋아짐에 따라 상속이 꼭 필요하지는 않아졌다. 상속이 많으면 가독성은 떨어지기 때문이다. (아무래도 상속된 기능, 변수를 확인하려면 부모클래스를 확인해야하고, 상속관계를 확인해야하고...)
# 상속
class Person:
def __init__(self, age, name) :
self.age = age
self.name = name
def info(self):
print('Name :', self.name, 'Age :', self.age)
class Student(Person):
def study(self):
print(self.name, 'is studying.')
부모클래스의 모든 매개변수를 그대로 가져오기 위해 super를 사용할 수 있다. 새로운 매개변수를 추가하면서 부모클래스의 매개변수를 모두 가져오기 위해서는 반드시 super를 사용해야 한다.
# 상속 (super 활용)
class Person:
def __init__(self, age, name) :
self.age = age
self.name = name
def info(self):
print('Name :', self.name, 'Age :', self.age)
class Student(Person):
def __init__(self, age, name, gender) :
super().__init__(age, name)
self.gender = gender
def study(self):
print(self.name, 'is studying.')
# 포함
class Person:
def __init__(self, age, name) :
self.age = age
self.name = name
def info(self):
return f'Name : {self.name} Age : {self.age}'
class Student:
def __init__(self, age, name) :
self.age = age
self.name = name
self.p = Person(self.age, self.name)
def study(self):
return self.p.info()
j = Student(23, 'Jade')
print(j.study()) # 결과 : 'Name : Jade Age : 23'
# 포함
class Person:
def __init__(self, age, name) :
self.age = age
self.name = name
class Student:
def __init__(self, person) :
self.person = person
def study(self):
print(f'{self.person.name} is studying.')
j = Student(Person(23, 'Jade'))
j.study() # 결과 : 'Jade is studying'
3. 추상화
추상적인 클래스를 생성해두고, 자식 클래스에서 구체적인 기능을 추가하는 것.
추상 클래스는 완전한 기능을 구현하지 않은 추상 method를 포함한 클래스를 말한다.
*참고 : 인터페이스 - 추상 클래스의 한 종류로, 추상화 정도가 높은 것을 말한다. 아무런 기능이 없는 단순 설계도이며, 그대로 상속해서는 하나의 인스턴스를 생성할 수 없고, 다수의 자식 클래스에서 각자 목적에 맞게 추상 클래스를 변형해서 사용한다.
추상클래스를 사용하면 복잡한 프로그램을 구현할 때 기준을 제시할 수 있고, 1차적인 설계를 해두고 각 기능에 대한 활용 여부는 차후 결정할 수 있다.
from abc import * # abc(abstract base class) module
# 추상 클래스
class Person(metaclass=ABCMeta):
# 추상 메소드 : @abstractmethod를 선언필요
@abstractmethod
def dowhat(self):
pass # 기능 없음
# 자식 클래스 1
class Student(Person):
def dowhat(self, subject):
self.subject = subject
print(f'studying {self.subject}')
# 자식 클래스 2
class Athlete(Person):
def dowhat(self, event):
self.event = event
print(f'playing {self.event}')
# object 생성
s = Student()
s.dowhat('Biology') # 결과 : 'studying Biology'
a = Athlete()
a.dowhat('baseball') # 결과 : 'playing baseball'
4. 다형성
상속처럼 부모 클래스의 기능을 차용하지만, 그 기능을 변형하여 사용하는 것을 말한다.
class Person:
def work(self):
print('person')
class Student(Person):
def work(self):
print('student') # work 메소드의 기능을 변경했다
s = Student()
s.work()
여기서 메소드 오버라이딩 개념이 사용된다. 부모클래스의 메소드를 재정의하여 사용하는 것을 메소드 오버라이딩이라고 한다.