객체 지향 프로그래밍(OOP: Object-Oriented Programming)은 우리가 살고 있는 실제 세계가 객체(Object)들로 구성되어 있는 것과 비슷하게, 소프트웨어도 객체로 구성하는 방법이다. 실제 세계에서는 사람, 텔레비전, 세탁기, 냉장고 등의 많은 객체가 존재한다. 객체들은 객체 나름대로의 고유한 기능을 수행하면서 다른 객체들과 상호작용한다.
즉 프로그램을 수많은 '객체(object)'라는 기본 단위로 나누고 이들의 상호작용으로 서술하는 방식이다. 객체란 하나의 역할을 수행하는 '메소드와 변수(데이터)'의 묶음으로 봐야 한다.
객체지향 프로그래밍 용어는 대부분의 프로그래밍 언어에서 비슷하게 사용하나, 일부 프로그래밍 언어에서는 용어를 조금 달리 사용한다. 예를 들어 자바에서는 메서드(Method)로 칭하는 것을 C++에서는 멤버 함수(Member Function)로 칭한다.
파이썬은 객체지향 개념을 적용할 수 있는 프로그래밍 언어이다. 객체지향 프로그래밍에서 가장 핵심적인 단어는 클래스(Class)이다. 클래스를 생성하는 방법은 다음과 같다.
class 클래스명:
# 관련 코드 구현
클래스는 '현실 세계의 사물을 컴퓨터 안에서 구현할려고 고안된 개념'이다. 현실 세계의 자동차를 예로들어보자. 자동차에는 색상, 속도 등의 속성이 있다. 또한 액셀 밟기(속도 올리기), 브레이크 밟기(속도 내리기) 등의 기능이 존재한다. 이것을 클래스로 표현하면 다음과 같다.
위와 같은 형태로 자동차 클래스를 만들 수 있다. 이와 같이 다양한 현실 세계의 사물들을 클래스로 표현할 수 있다. 위의 예시에서 자동차의 속성은 지금까지 사용했던 변수처럼 생성하는데 이를 '필드(Field)'라고 한다. 또한 자동차의 기능은 함수의 형식으로 구현한다. 이 때 클래스 안에서 구현된 함수는 함수라 칭하지 않고 메소드(Method)라고 한다. 위의 그림을 코드로 나타내면 다음과 같다.
class Car :
# 자동차의 필드
색상
현재 속도 = 0
# 자동차의 메서드
def upSpeed(증가할 속도량) :
# 현재 속도에서 증가할 속도량만큼 속도를 올리는 코드
def downSpeed(감소할 속도량)
# 현재 속도에서 감소할 속도량만큼 속도를 내리는 코드
다시 Car class의 완전한 파이썬 코드는 다음과 같다.
class Car :
color = “”
speed = 0
def upSpeed(self, value) :
self.speed += value
def downSpeed(self, value) :
self.speed -= value
출력 결과:
위의 Car class의 출력 결과는 나타나지 않는다. 위의 코드는 Car class의 설계도와 같다. 즉 붕어빵의 '틀'과 같은 존재이다.
여기서 'self' 라는 단어가 나온다. self는 클래스 자기 자신의 주소를 가지고 있다. self.speed는 speed를 의미한다. 즉 자신의 클래스의 speed 멤버 변수(필드)를 의미한다. 그런데 self.speed를 사용할려면 self 라는 매개변수를 받아야하는 것 처럼 보인다. 하지만 매개 변수 self를 실제로 전달받지 아니하며 value값만 전달받는다. 즉 upSpeed() 메서드의 매개변수는 value 하나뿐이다. 또한 self 라는 단어는 객체를 생성 해야지만 비로소 활성화가 이루어진다.
메서드의 첫 번째 매개변수 self를 사용하는 이유는 메서드 안에서 필드(speed, color 등)에 접근하기 위해서이다. 그래서 self를 매개변수로 보기보다는 메서드 안에 무조건 써야하는 것으로 이해하는 편이 좋다. 하지만 메서드 안에서 필드의 접근할 일이 없다면 self는 생략이 가능하다. Car class의 메서드와 필드는 얼마든지 삭제, 추가가 가능하다.
class Car :
color = “”
speed = 0
def upSpeed(self, value) :
self.speed += value
def downSpeed(self, value) :
self.speed -= value
# 추가된 메서드
## 필드에 접근할 일이 없으므로 self 생략이 가능하다.
def printMessage() :
print("시험 출력이다.")
앞에서는 자동차의 클래스를 만들었다. 이는 자동차의 설계도를 그린 것에 불과하다. 설계도가 있다고 해서 자동차를 만든 것은 아니다. 이 설계도를 기반으로 실제 자동차를 제작하는 작업을 해야한다. 이렇게 생산 되는 자동차를 인스턴스(instance)이며 객체(Object)이다.
자동차 설계도를 그린 후에는 실제로 자동차를 여러 대 생산하는 것처럼, 클래스를 만든 후에는 인스턴스를 생산해야한다.
위의 코드를 기반으로 자동차 세 대를 생성하는 코드는 다음과 같다. 이와 같은 코드를 작성함으로써 3대의 자동차 인스턴스를 생성하였으며, 각각 고유의 색상(color), 속도(speed)의 필드를 가진다.
myCar1 = Car()
myCar2 = Car()
myCar3 = Car()
자동차 인스턴스가 생성되면 각 자동차는 독립적인 메모리 공간을 가지게된다. 그러므로 각 인스턴스에는 고유한 필드가 존재하며, 각각 별도의 값 대입이 가능하다. 아래와 같이 색상(color) 필드에 색을, 속도(speed) 필드에 속도를 초기화 할 수 있다.
myCar1.color = "빨강"
myCar1.speed = 0
myCar2.color = "파랑"
myCar2.speed = 0
myCar3.color = "노랑"
myCar3.speed = 0
처음의 Car class 에서 upSpeed(), downSpeed(), printMessage() 메서드 3개를 넣었다. 메서드 또한 각 인스턴스 마다 별도로 존재한다고 생각하면 된다. 메서드의 호출 또한 필드의 값 대입 처럼 '인스턴스 명.메서드 명()' 의 형식을 사용한다. 아래는 myCar1에 속도를 30을 증가시키며, myCar2은 속도를 60을 감소 시켰다.
myCar1.upSpeed (30)
myCar2.downSpeed(60)
myCar3.printMessage()
출력결과
시험 출력이다.
앞의 코드를 정리하면 다음과 같다.
# 클래스 선언 부분
class Car :
# 필드(멤버 변수)
color = “”
speed = 0
# 메소드
def upSpeed(self, value) :
self.speed += value
def downSpeed(self, value) :
self.speed -= value
# 메인코드 부분
myCar1 = Car()
myCar1.color = "빨강"
myCar1.speed = 0
myCar2 = Car()
myCar2.color = "파랑"
myCar2.speed = 0
myCar3 = Car(())
myCar3.color = "노랑"
myCar3.speed = 0
myCar1.upSpeed(30)
print("자동차1의 색상은 %s이며, 현재 속도는 %dkm입니다." % (myCar1.color, myCari.speed))
myCar2.upSpeed(60)
print("자동차2의 색상은 %s이며, 현재 속도는 %dkm입니다.” % (myCar2.color, myCar2.speed))
myCar3.upSpeed(0)
print("자동차3의 색상은 %s이며, 현재 속도는 dkm입니다." % (mycar3.color, myCar3.speed))
출력 결과:
자동차1의 색상은 빨강이며, 현재 속도는 30km입니다.
자동차2의 색상은 파랑이며, 현재 속도는 60km입니다.
자동차3의 색상은 노랑이며, 현재 속도는 0km입니다.
일반적으로 클래스의 사용의 절차는 아래와 같다.
생성자(Constructor)는 인스턴스를 생성하면 무조건 호출되는 메서드이다.
myCar1 = Car()
myCar1.color = "빨강"
myCar1.speed = 0
myCar1를 생성한 후 색상, 속도 필드를 초기화 해줬다. 이러는 것보다 인스턴스를 생성과 동시에 초기화가 된다면 훨씬 코드도 간결해지며, 필드에 초기화 값을 대입하는 것도 잊어버리지 않을 것이다. 이렇게 인스턴스를 생성하면서 필드값을 초기화시키는 메서드를 생성자라고 한다.
생성자는 __init__() 라는 이름을 가진다. init()는 앞뒤로 언더바(__)가 2개 씩 있다. init는 initialize의 약자로 '초기화' 의미를 가진다. 언더바가 2개 붙은 것은 파이썬에서 예약해 놓은 것이므로, 프로그램을 작성할 때는 이 이름을 사용해서 새로운 함수나 변수명을 만들지 말아야 한다는 것이다.
파이썬에서의 생성자의 규칙이 3가지 있다. 먼저 생성자의 명은 __init__()으로 작성해야한다.(다른 언어에서는 보통 클래스 명을 생성자 명으로 한다.) 그리고 생성자의 리턴 값은 존재하지 않는다. 마지막으로 생성자는 여러 개가 존재해서는 안된다.
파이썬에서는 2개 이상의 생성자를 만들 수 없다.
참고로 다른 언어에서는 같은 메서드명으로 다른 일을 하게끔 만드는 할 수 있는데 이를 '오버로딩(OverLoding)'이라 한다. 이는 매개변수의 타입과 개수에 따라서 같은 이름의 메서드라도 다른 메서드가 호출이 되는 형태를 지칭한다.
처음의 Car class 에서는 다음과 같이 생성자를 만들 수 있다. 이것은 매개변수가 없는 생성자이다.
class Car :
color = “”
Speed = 0
def _init__(self);
self.color = "빨강"
self.speed = 0
인스턴스를 생성하면 자동으롤 생성자를 호출한다.
myCar1 = Car()
print(myCar1.color)
print(myCar1.speed)
출력 결과:
빨강
0
# 클래스 선언 부분 #
class Car :
color =
speed = 0
# 기본 생성자
def __init__(self):
self.color = “빨강”
self.speed = 0
def upSpeed(self, value) :
self.speed += value
def downSpeed(self, value) :
self.speed -= value
#메인 코드 부분
myCar1 = Car()
myCar2 = Car()
print("자동차1의 색상은 %s이며, 현재 속도는 %dkm입니다.” % (myCar1.color, myCar1.speed))
print(“자동차2의 색상은 %s이며, 현재 속도는 %dkm입니다." % (myCar2.color, myCar2.speed))
출력 결과:
자동차1의 색상은 빨강이며, 현재 속도는 0㎞입니다.
자동차2의 색상은 빨강이며, 현재 속도는 0km입니다.
위의 코드에서 __init__()은 self 외에 별도의 매개변수를 사용하지 않았다. 이렇게 매개변수가 self만 있는 생성자를 '기본 생성자' 라고 한다.
처음에 클래스를 작성할 때는 __init__() 라는 생성자를 만들지 않고도 문제가 없었다. 없음에도 불구하고 인스턴스를 생성하고 필드의 활동이 가능하였다.
이는 파이썬 인터프리터에서 클래스 안에 생성자가 1개라도 존재하지 않으면 자동으로 아무런 값을 설정하지 않은 기본생성자를 추가해주기 때문이다.
생성자도 다른 메서드 처럼 매개변수를 사용할 수 있다. 그리고 인스턴스를 생성할 때 초기값을 매개변수로 넘기는 방법도 사용한다.
주의할 점은 매개변수 없이 Car 인스턴스를 생성하면 오류가 발생한다. 그 이유는 기본 생성자가 존재하지 않기 때문이다. 기본적으로 자바에서는 기본생성자, 매개변수가 있는 생성자 등 여러 개를 생성할 수 있지만 파이썬에서는 그렇지 아니하다. 생성자가 하나만 존재한다는 것이다.
class Car :
color =
speed = 0
# 매개변수가 있는 생성자
def __init__(self, color, speed) :
self.color = color
self.speed = speed
def upSpeed(self, value) :
self.speed += value
def downSpeed(self, value) :
self.speed -= value
#메인 코드 부분
myCar1 = Car("빨강", 30) # 매개변수가 있는 생성자 호출
myCar2 = Car("파랑", 60) # 매개변수가 있는 생성자 호출
print("자동차1의 색상은 %s이며, 현재 속도는 %dkm입니다.” % (myCar1.color, myCar1.speed))
print(“자동차2의 색상은 %s이며, 현재 속도는 %dkm입니다." % (myCar2.color, myCar2.speed))
출력 결과:
자동차1의 색상은 빨강이며, 현재 속도는 30㎞입니다.
자동차2의 색상은 파랑이며, 현재 속도는 60km입니다.
__str__() 메소드는 파이썬의 내장함수로써, 클래스 내의 정보를 '문자열' 화 하여 서로 다른 객체들 간의 정보를 전달하는데 사용된다. 무조건 문자열만 반환이 가능하다.
class ComputerMoniter:
Inch = 0
company = ''
count = 0
# 매개변수가 존재하는 생성자
def __init__(self,company, inch):
self.company = company
self.Inch = inch
# 클래스 변수의 지정
ComputerMoniter.count += 1
# __str__() 내장 메소드를 통해 인스턴스의 변수들을 출력한다.
def __str__(self):
print("컴퓨터의 제조 회사는 %s 이며 사이즈는 '%s'inch이다. (%s대)" %(self.company, self.Inch, ComputerMoniter.count))
if __name__ == '__main__':
moniter1 = ComputerMoniter('삼성',32)
moniter1.__str__()
아래 코드에서 getter() 메소드가 있는데 이것은 단순히 객체지향개념에서 캡슐화라는 개념이 존재한다. 즉 다시 말해서 클래스의 필드를 외부로 노출시키는 것을 방지하는 것을 의미한다. 하지만 필드 값을 수정을 하게 하는 setter() 메서드도 있지만 웬만한 클래스에서는 getter() 메서드만 제공한다. 최소한 필드의 값은 읽어가야지만 연계된 프로그램에서 그 값을 활용할 수가 있기 때문이다.
언더바(__) 붙이면 private 성질을 가지며, 이는 클래스 내부에서만 접근이 가능하게 한다. 즉 외부에서의 접근을 막는다. 이를 출력하기 위해 getter 함수를 사용한다.
--가 붙지 아니하면 public 성질을 지닌다. 클래스 내부이건 외부이건 다 접근이 가능하다. 만일 중요한 정보라면 접근하는 것을 제한하기 위해 언더바(__)을 붙이고 관리한다.
언더바가 붙은 경우 private 성질을 지니기 때문에 자신의 클래스에서만 작성하여 사용한다. 상속도 안되며 마지막에 붙는 메서드이다.
class Car :
name = “”
speed = 0
# 매개변수가 있는 생성자
def __init__(self, name, speed) :
self.name = name
self.speed = speed
# 아래 2개의 메서드는 getter() 메서드라고 한다.
def getName(self):
return self.name
def getSpeed(self) :
return self.speed
#변수 선언 부분
car1, car2 = None, None
#메인 코드 부분 #
car1 = Car("아우디", 0)
car2 = Car("벤츠", 30)
print("%s의 현재 속도는 것입니다." % (car1.getName(), car1.getSpeed()))
print("%s의 현재 속도는 0입니다." % (car2.getName(), car2.getSpeed()))
출력 결과:
아우디의 현재 속도는 0입니다.
벤츠의 현재 속도는 30입니다.
지금까지 사용한 필드는 모두 인스턴스 변수라 할 수 있다. 예를 들어 Car class의 color, speed 필드는 인스턴스 변수이다. 인스턴스 변수라는 것은 인스턴스를 생성해야 비로소 사용할 수 있는 변수이다.
class Car :
color = “” # 필드 : 인스턴스 변수
Speed = 0 # 필드 : 인스턴스 변수
이 두 필드는 클래스 안에 있으며, 아직 실제 공간이 할당된 것이 아니다. 클래스는 설계도에 해당하므로 실제 자동차의 색상, 속도가 구현되지 않은 것과 마찬가지다.
myCar1 = Car()
myCar2 = Car()
다음과 같은 코드를 작성하고 나서야 비로소 메모리가 할당된다.
이제 myCar1은 실제로 존재하는 자동차이며, myCar1 안에는 color와 speed의 메모리에 공간이 만들어진 것이다. 또 myCar2도 color, speed의 메모리에 공간이 독립적으로 만들어진 것이다.
결론은 인스턴스 변수는 메모리에 독립적인 공간을 가지고 있으며 반드시 인스턴스가 생성되어야 사용할 수 있다.
클래스 변수는 클래스가 생성될 때 이미 메모리 공간에 할당된 변수를 의미한다. 그래서 클래스 변수는 인스턴스에 별도의 공간을 할당하지 않고, 여러 인스턴스가 클래스 변수의 공간을 함께 사용한다. 이렇기에 클래스 변수를 공유 변수라고도 한다.
클래스 변수를 만드는 방법은 인스턴스 변수와 동일하다. 다만 클래스 변수에 접근할 때는 '클래스 명. 클래스 변수명' 또는 '인스턴스 클래스.변수명(권장하지 않음)' 방식으로 접근한다. 그리고 인스턴스를 생성하여도 추가로 메모리 공간을 할당하지 않고 인스턴스 변수와 다르게 메모리의 다른 공간에 이미 생성되어 있는 공간을 공유한다.
클래스 변수는 인스턴스를 생성하기 전에도 메모리에 존재하기에 생성 전에도 접근이 가능하다.
인스턴스 변수는 생성 전에는 생성, 접근이 불가하다.
# 클래스 선언 부분 #
class Car :
color = “” # 인스턴스 변수
speed = 0 # 인스턴스 변수
count = 0 # 클래스 변수
def __init__(self) :
self.speed = 0
Car.count += 1
# 변수 선언 부분 #
myCar1, myCar2 = None, None
# 메인 코드 부분 #
myCar1 = Car()
myCar1.speed = 30
print("자동차1의 현재 속도는 %dkm, 생산된 자동차는 총 %d대입니다." % (myCar1.speed, Car.count))
myCar2 = Car()
myCar2.speed = 60
print("자동차2의 현재 속도는 %dkm, 생산된 자동차는 총 %d대입니다." % (myCar2.speed, myCar2.count))
출력결과
자동차1의 현재 속도는 30m, 생산된 자동차는 총 1대입니다.
자동차2의 현재 속도는 60㎞, 생산된 자동차는 총 2대입니다.
클래스 변수 count를 선언하고 0으로 초기화한다. 그리고 생성자 안에서 클래스 변수인 count 에 접근하기 위해 'Car.count += 1'를 작성하였다. 생성자는 인스턴스를 생성할 때 작동하므로, 인스턴스가 생성될 때 마다 count는 1씩 증가한다.
메인 코드 부분에서 클래스 변수를 사용하려고 Car.count나 myCar2.count를 모두 이용할 수 있다. 즉 둘 다 클래스 변수에 접근한다. 하지만 중요한 것은 여기서 객체지향 개념으로 따져볼 때는 클래스 변수는 메모리 상단에 클래스 정보와 함께 같이 공간을 잡는다.
그렇기에 접근할 때는 웬만하면 '클래스명.클래스변수명'으로 접근하는 것이 올바른 표현이며 가독성이 높다.
파이썬에서의 인스턴스 변수와 클래스 변수의 구분은 클래스 안에서 필드에 접근할 때 앞에 self를 붙이면 인스턴스 변수가 되고, 앞에 클래스명을 붙이면 클래스 변수를 생성하게 되는 것이다.
클래스 변수를 인스턴스 객체로 부터 참고하는 건 안하는 게 좋다.