객체지향프로그래밍이란 프로그램 설계방법론이자 개념의 일종이다.
프로그램을 데이터와 처리방법으로 나누는 것이 아니라 프로그램을 수많은 객체(object)라는 기본 단위로 나누고 이 객체들의 상호작용으로 서술하는 방식이다.
객체란 하나의 역할을 수행하는 method와 변수(데이터), 클래스, 인스턴스, 함수 등을 의미한다.
OOP의 기본전제는 기능(함수,변수)재사용이 가능하도록 설계 및 프로그래밍을 한다는 것이다.
최소 비용으로 최대 효율을 얻기 위해 개발된 개념이라고 할 수 있다.
OOP의 기본 개념은 설계(사람이 이해하는 방식)와 구현할 소스코드(컴퓨터가 이해하는 방식)간의 상호이해가 중요하다는 것이다. 하드웨어와 소프트웨어의 성능의 증가로 CPU 성능이 증가하고 소프트웨어 다중 실행이 가능해지면서 OOP의 모든 기능을 활용할 필요는 없어졌다.
OOP의 단점:
OOP 개념이 나오기 전에는 배열과 함수, 변수를 많이 생성하고 활용하여 최대한 많은 기능을 적은 양의 소스코드파일에 넣었다.
속성과 기능이 증가할 때마다 배열과 함수를 계속 생성해야했기 때문에 소스코드의 관리가 비효율적이었다.
이에 따라 속성과 기능을 객체(object)라는 최소단위로 분리하는 OOP의 개념이 나오기 시작했다.
프로그래밍에서 OOP의 개념을 항상 활용하는 것은 아니다. 데이터분석을 진행하는 경우, 모듈과 라이브러리를 활용한 분석 인사이트가 중요하다.
Data-driven(데이터기반 의사결정), 컴퓨터하드웨어성능, 데이터양 증가에 따라 OOP활용도 증가하였다.
# 절차 프로그래밍(procedure programming, 일명 pp)
# 조건 또는 기능이 증가할 때마다 함수와 변수 같은 요소가 계속 증가할 수 있으므로 비효율적이다.
#케이스 1: 함수 활용
def carAttribute():
speed = 50
color = 'black'
model = 'CarModel'
return speed, color, model
#return 값을 통해 함수 요소값을 확인할 수 있다.
print('Car Attribute:', carAttribute())
#케이스 2: 변수 활용
speed =50
color ='black'
model = 'CarModel'
#해당 변수를 각각 명시해주어 확인할 수 있다.
print('Car Attribute: ', speed,color,model)
#객체지향프로그래밍(Object oriented Programming, OOP)
#절차 프로그래밍과 다르게 기능별로 수행되기 때문에 클래스 선언 순서와 관계없이 실행된다.
class Bus:
def __init__(self, speed, color):
self.speed = speed
self.color = color
def drive_bus(self):
self.speed = 70
class Car:
def __init__(self, speed, color, model):
self.speed = speed
self.color = color
self.model = model
def drive(self):
self.speed = 50
myCar = Car(0, 'green', 'benz')
print('-------Car Object Result---------')
print('car speed: ',myCar.speed)
print('car color: ',myCar.color)
print('car model: ',myCar.model)
print('bus color: ',myBus.color)
myBus = Bus(0,'black')
print('-------Bus Object Result---------')
print('bus color: ',myBus.color)
#운전 중이 아니므로 speed는 0을 출력한다.
print('---speeds1---')
print('Car speed: ', myCar.speed)
print('Bus speed: ', myBus.speed)
#call object 'Car','Bus' method
myCar.drive()
myBus.drive()
#운전 중이 아니므로 speed는 0을 출력한다.
print('---speeds2---')
print('Car speed by drive: ', myCar.speed)
print('Bus speed by drive: ', myBus.speed)
일반적인 정의: 객체의 속성과 행위(기능)을 하나로 묶고 실제 구현 내용의 일부를 은닉하는 것.
기본 개념: 내부 속성(변수)과 함수를 하나로 묶어서 클래스로 선언하는 일반적인 개념
- 캡술화의 형태로 코드를 작성하지 않으면 특정 기능(함수, 변수)에 직접 접근해야한다.
- 기능이 많아질수록 재사용의 개념을 활용하기 어려워진다.
_function(x)
: 다른 곳에 import 할 수는 없지만 class 내의 함수로서는 부를 수 있다.__function(x)
: _ClassName__method
로 Mangling된다. ClassName.__method
로 접근이 불가하다.#캡슐화 코드
class Encap:
def __init__(self, value):
self.value = value
print('init: ',self.value)
def _set(self):
print('set: ',self.value)
def printTest(self):
print('printTest: ',self.value)
#def __printTest2(self):
# print('printTest: ',self.value)
# object 생성
e = Encap(10) ##init: 10
# object 실행_케이스1
e.__init__(20) ##init: 20
e._set() ##set: 20
e.printTest() ##printTest: 20
#e.printTest2() ##접근 불가
print('\n')
# 케이스2
e.__init__(30) ##init: 30
e._set() ##set: 30
e.printTest() ##printTest: 30
상위 클래스의 모든 기능(함수, 변수)을 재사용할 수 있다.
#상속 코드
#클래스 선언
class Person:
def __init__(self, name):
self.name=name
class Student(Person):
def study(self):
print(self.name + ' studies hard.') #Person 클래스 상속받음(name 변수를 파라미터로 재사용)
class Employee(Person):
def work(self):
print(self.name + ' works hard.') #Person 클래스 상속받음(name 변수를 파라미터로 재사용)
#object 생성
s = Student('Dave')
e = Employee('David')
#object 실행
s.study() #Dave studies hard.
e.work() #David works hard.
다른 클래스의 일부 기능(함수)만을 재사용한다.
'개는 몸을 가지고 있다.'라는 관계로 설명된다.
# 포함코드
#클래스 선언
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def printPerson(self):
print('Persom_printPerson')
class Student(Person):
def __init__(self, name, age, id):
Person.__init__(self, name, age)
Person.printPerson(self)
self.id = id
def test(self, score):
if score > 80:
print(self.name+' studied hard.')
else:
print(self.name+' needs supplementary lessons.')
#object 생성
#한 번 생성된 object는 파라미터가 변하지 않는 이상 출력값 또한 변하지 않는다.
s = Student('Dave', 20, 1)
#object 실행
print('student's name: ', s.name) ## student's name: Dave
print('student's age: ', s.age) ## student's age: 20
print('student's id: ', s.id) ## student's id: 1
print('\n')
#object 한 개 더 생성하기
s2 = Student('Jamie', 25, 2)
#object 실행
print('student's name: ', s2.name) ## student's name: Jamie
print('student's age: ', s2.age) ## student's age: 25
print('student's id: ', s2.id) ## student's id: 2
#점수 입력
print('---test score result---')
s.test(50) ##Dave needs supplementary lessons.
s2.test(90) ##Jamie studied hard.
class Bill():
def __init__(self, description):
self.description = description
class Tail():
def __init__(self, length):
self.length = length
class Duck():
def __init__(self, bill, tail):
self.bill = bill
self.tail = tail
def about(self):
print(f'This duck has a {self.bill.description} and a {self.tail.length}.')
#object 생성
duck = Duck(bill('bill object'), Tail('tail object'))
#object 실행
duck.about()
추상화(abstraction)은 복잡한 내용에서 핵심적인 개념 및 기능을 요약하는 것을 말한다.
- object 기능에 따라 추상 클래스(상위 클래스)를 상속받아 개별적으로 클래스(하위클래스)를 생성한다.
- 기본적으로 추상메소드를 선언하며 실제 실행되는 기능은 보여지지 않는다.
- 실제 실행되는 기능은 선언된 추상클래스를 상속받은 다른 클래스의 메소드에서 확인할 수 있다.
- 추상클래스를 사용하는 이유 : 대형 프로젝트를 진행하는 경우 또는 프로그램이 복잡해지는 경우 1차적인 설계를 위해 기능을 추상화시켜놓고 활용여부는 차후 결정하기 위함이다.
from abc import * #abc(abstract base class) 모듈의 클래스와 메소드를 갖고 온다.
#추상 클래스
class People(metaclass=ABCMeta):
#추상 메소드
@abstractmethod #추상메소드에는 abstractmethod를 선언해주어야 함.
def character(self):
pass
#상속받는 클래스
class Student(people):
def character(self, pow, think):
self.pow = pow
self.think = think
print('체력: {0}'.format(self.pow))
print('생각: {0}'.format(self.think))
#student object 생성
peo1 = Stuedent()
print('Student: ',peo1)
#student object 실행
peo1.character(30,10)
print(peo1)
다형성은 구현되는 하위클래스에 따라 클래스를 다르게 처리하는 기능이다.
- 상속과 유사하다고 느껴질 수 있지만, 상속은 상위클래스의 기능(함수, 변수)을 재사용한다.
- 아래의 그림과 같이 다형성은 상위클래스의 기능을 변경하여 사용하는 것이다.(그대로 재사용하지 않는다.)
class Person:
def run(self):
print("I'm a human: ", end='')
print('run')
def play(self):
print("I'm a human: ", end='')
print('play')
class Student(Person):
def run(self):
print("I'm a student: ", end='')
print('fast run')
def play(self):
print("I'm a student: ", end='')
print('play')
class teacher(Person):
def teach(self):
print("I'm a teacher: ", end='')
print('teach')
def play(self):
print("I'm a teacher: ", end='')
print('teach play')
# 리스트를 생성한다.
number = list()
# 생성한 리스트에 다형성 개념을 위해 다른 클래스(Student, teacher)가 상위 클래스(Person)를 참조할 수 있도록 한다.
number.append(Student()) # 리스트 끝에 서브 클래스 Student()를 넣습니다.
number.append(teacher()) # 다시 리스트 끝에 서브 클래스 teacher()를 넣습니다.
print("=========")
for Runner in number:
Runner.run() # 상위클래스인 Person의 run은 상속하여 사용하지만 내용은 다르다.
print("=========")
for Player in number:
Player.play() # 상위클래스인 Person의 play는 상속하여 사용하지만 내용은 다르다.
결과
=========
I'm a student: fast run
I'm a human: run
=========
I'm a student: play
I'm a teacher: teach play
헷갈리지 않도록 코드를 작성하자. 연산자를 효율적으로 활용하자.
연산자 1개(is not) : A는 B가 아니다.
연산자 2개(not, is): A가 아닌 것은 None이다.
# 오해하지 않도록 코드를 작성하자.
# foo: 프로그래밍 상에서 임시로 변수이름을 지정해줘야 할 때 주로 쓰이는 변수 이름이다.
foo = ''
# 가독성 좋음
if foo is not None: # 한 번만 해석하면 된다. (A는 B가 아니다.)
print('가독성 좋음')
# 가독성 좋지 않음
if not foo is None: # 두 번 해석해야된다. (A가 아닌 것은 None이다.)
print('가독성 좋지 않음')