Everything in Python is an object, and almost everything has attributes and methods.
파이썬에서는 부울(bool), 정수, 실수, 문자열, 배열, 딕셔너리, 함수, 모듈, 프로그램 등 모든 것이 객체이다. 파이썬에서 object라 불리는 것들은 모두 변수에 할당될 수 있고, 함수의 인자로 넘겨질 수 있는 것들이다.
변수와 같은 객체를 담는 것을 활용한 프로그래밍을 객체 지향 프로그래밍(OOP: Object Oriented Programming) 라고 한다.
컴퓨터 언어가 생겨나던 초창기에 많은 언어들은 데이터(변수)와 함수만을 표현해 왔다. 그런데 조금 큰 프로그램을 작성하다 보니 문제가 생겼다. 예를 들어 A라는 자동차와 B라는 자동차를 각각의 변수로 만들고자 한다면 car_a_tire
, car_b_tire
, car_a_window
, car_b_window
, ... 처럼 많은 변수들이 같은 계층에서 만들어졌기 때문이다. 함수 역시 car_a_run()
, car_b_run()
, car_a_stop()
, car_b_stop()
, ... 처럼 계속 늘어나야 했다. 이렇게 한없이 변수와 함수가 늘어나는 문제가 발생한다.
컴퓨터를 연구하던 사람들은 만약 Car
라는 묶음을 만들어 놓고 Car
묶음 안에 tire
를 넣어 놓으면 car_a.tire
, car_b.tire
처럼 사용할 수 있으니 계층이 달라지고 변수를 무한히 펼쳐 놓지 않아도 된다는 점을 깨닫게 된다. 그래서 그 묶음의 형식을 클래스(Class)라고 소개하고 실제로 만들어 낸 각각의 묶음(car_a
, car_b
)을 객체(Object)라고 부르게 된다.
파이썬에서 개발자가 객체를 직접 설계하기 위해서는 class
키워드를 이용한다.
1) 클래스 선언
class Car:
pass
class Car():
pass
#id(Car)는 여러번 호출해도 같은 값이 얻어진다.
print(id(Car))
print(id(Car))
#id(Car())는 Car()가 호출될 때마다 다른 값이 얻어진다.
print(id(Car()))
print(id(Car()))
# 두 객체의 type을 살펴보자.
print(type(Car))
print(type(Car()))
/----------------------------------------------/
# 출력
94415969383104
94415969383104
140304710955408
140304710558096
<class 'type'>
<class '__main__.Car'>
이처럼 클래스 선언은 class
키워드를 이용한다. class
키워드를 쓰고 클래스 이름을 쓴 다음 :
(콜론)을 씁니다. 클래스 이름 다음에 소괄호를 적어 주어도 되지만 보통은 생략한다.
위 코드에서 보면, Car
라는 클래스 자체도 객체이고, Car()
를 호출할 때마다 새로운 Car
타입의 객체가 생성됩니다. 둘 다 id가 존재하기 때문입니다. 그러나, id(Car)
의 값은 몇 번 호출되더라도 동일하지만 id(Car())
는 호출될 때마다 다른 id 값을 가진다는 것도 알 수 있다.
Car
라는 클래스는 type
유형의 객체이다. Car()
가 호출될 때 Car
타입의 객체가 생성한다.
클래스를 이용하려면 클래스로 객체를 만들어 주어야 하는데, 이를 인스턴스화라고 한다.
인스턴스화는 어떻게 할까? 선언한 클래스 이름에 괄호를 적어 준다. 그리고 생성된 인스턴스를 적당한 변수에 할당해서 사용한다.
mycar = Car()
mycar2 = Car()
print(id(mycar))
print(id(mycar2))
/---------------/
# 출력
140304710557952
140304710560160
mycar
변수에 Car
클래스의 인스턴스를 할당한 것이다. "클래스를 호출(call) 했다"라는 표현을 쓰기도 한다. 위 코드에서는 인스턴스가 생성될 때마다 객체를 할당받은 변수들에게 다른 id가 부여되는 것을 알 수 있다.
표기법!
PEP는 Python Enhancement Proposals의 약자로 파이썬 개선사항을 위한 공식 문서이다. 그중에서도 PEP8은 파이썬 코딩 스타일(변수명, 코드 표기법 등등)을 개정한 문서이다. 해당 문서에서는 아래와 같이 표기할 것을 권고한다.
클래스명 표기법: 카멜 케이스
- 카멜 케이스: 각 단어의 앞 글자를 대문자로 쓸 것.
- 예시: mycar —> MyCar
함수명 표기법: 스네이크 케이스
- 스네이크 케이스: 단어는 소문자로 쓰고 각 단어의 연결은 언더바(_)를 사용할 것
- 예시: mycar —> my_car
그리고 추가로 클래스명은 주로 명사로, 함수명은 주로 동사로 명명한다.
클래스에서 속성과 메서드는 어떻게 구현할까?
def
키워드로 나타낸다.색상
은 "red", 종류
는 "sports car"라는 속성을 갖고 주행
과 가속
이라는 동작을 갖는 클래스를 정의해 보자.
속성은 변수, 동작은 다른 말로 메서드이다.
class Car:
color = 'red'
category = 'sports car'
def drive(self):
print("I'm driving")
def accel(self, speed_up, current_speed=10):
self.speed_up = speed_up
self.current_speed = current_speed + speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
color
에는 'red'를 category
에는 'sports car'를 할당def
키워드를 이용self
값을 적어 주어야 한다.drive()
라는 메서드명으로 정의하고 self외에 인자는 없다.accel()
라는 메서드명으로 정의하고 현재 속도와 가속할 값을 인자로 받는다.current_speed
, 가속 인자명 = speed_up
accel
메서드에 self.speed_up
, self.current_speed
를 인자로 준다.# Car 클래스입니다.
class Car:
'''
속성은 클래스의 상태를 나타낸다.
색상: 빨강
종류: 스포츠 카
'''
color = 'red'
category = 'sports car'
'''
동작은 메서드로 나타낸다.
'''
def drive(self):
'''
주행 메서드
'''
print("I'm driving")
def accel(self, speed_up, current_speed=10):
'''
가속 메서드
:param speed_up: 현재속도
:param current_speed: 가속
:type speed_up: string
:type current_speed: string
'''
self.speed_up = speed_up
self.current_speed = current_speed + speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
클래스의 속성에 접근하기 위해서는 인스턴스 객체에 .
을 쓰고 그 뒤에 속성명을 적는다.
mycar = Car()
print(mycar.color)
/----------------/
# 출력
red
메서드를 호출하는 방법은 속성 접근과 유사하다. 인스턴스 객체에 .
을 쓰고 그 뒤에 메서드 명을 적어준다. 클래스의 메서드는 클래스 내에 정의된 함수라고 말할 수 있다. 따라서, 함수를 호출하는 것과 동일하게 함수 이름을 쓰고 소괄호 "()"를 적는다.
mycar.drive()
mycar.accel(5)
/------------/
# 출력
I'm driving
speed up 5 driving at 15
인스턴스의 속성으로 사용하고 싶은 변수는 self.
을 써준다. self
인자를 통해 선언된 객체의 값이란 의미이다.
객체 안에서 self
를 사용하면 인스턴스 객체의 고유한 속성을 나타낼 수 있다. 클래스가 아닌 self
, 즉 인스턴스화된 객체 자신의 속성이라는 뜻이다.
self
요약
self
는 자기 자신이다.- 클래스에 의해 생성된 객체(인스턴스)를 가리킨다.
- 클래스의 메서드는 인자로 해당 인스턴스(
self
)를 받아야 한다.- 메서드를 호출할 때는
self
인자를 전달하지 않는다.self
의 값은 인터프리터가 제공한다.- 인스턴스 변수를 정의할 때에는 접두사
self.
을 붙여준다.
클래스에 의해 만든 인스턴스 객체의 속성값을 사용자는 어떻게 초기화할 수 있을까? __init__
을 사용해서 만들 수 있다.
class Car2:
def __init__(self, color, category):
self.color = color
self.category = category
def drive(self):
print("I'm driving")
def accel(self, speed_up, current_speed=10):
self.speed_up = speed_up
self.current_speed = current_speed + self.speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
__init__
메서드 안에 인자를 전달함으로써 인스턴스 객체의 속성을 초기화할 수 있다.__init__
메서드 안에 정의된 속성(변수) color
와 category
는 클래스를 인스턴스화할 때 값을 설정할 수 있다.__init__
함수는 생성자(constructor)라고 한다.__init__
역시 def
키워드로 정의한다. 즉, 클래스 안의 메서드이므로 self
문법 역시 사용해야한다.car2 = Car2('yellow', 'sedan')
print(car2.color)
car2.category
/----------------------------/
# 출력
yellow
'sedan'
__init__
이라고 쓰고, "던더(Double Under) 이닛"이라고 발음__init__
처럼 앞뒤에 언더바(_)가 두 개씩 있는 메서드를 매직 메서드라고 한다.클래스 내 변수를 선언하는 방법은 어떻게 될까? 클래스에서 변수를 선언하는 방법은 2가지가 있다. 하나는 보통 변수와 동일하게 변수명을 쓰고 값을 할당하는 방법, 두 번째는 __init__
메서드 안에 self.
와 함께 설정하는 방법이다. 아래는 예제 코드이다.
class Car:
Manufacture = "India"
def __init__(self, color, category='sedan'):
self.color = color
self.category = category
car1 = Car('red','sports car')
car2 = Car('white')
print(car1.Manufacture, car1.color, car1.category)
print(car2.Manufacture, car2.color, car2.category)
/------------------------------------------------/
# 출력
India red sports car
India white sedan
Manufacture
같은 변수를 클래스 변수라고 합니다. self.color
와 같은 변수를 인스턴스 변수라고 한다.
Manufacture
는 클래스 변수Manufacture
속성은 car1
과 car2
가 공유__init__()
안에서 self를 사용해 선언된 변수를 인스턴스 변수라고 함. 객체가 인스턴스화될 때마다 새로운 값이 할당되며 서로 다른 객체 간에는 값을 공유할 수 없음.color
와 category
는 인스턴스 변수color
와 category
속성은 car1
과 car2
가 공유하지 않음.즉! 객체 단위로 변경되는 변수는 인스턴스 변수로 선언
부모 클래스가 자식 클래스에게 '멤버변수'와 '메서드'를 물려주는 걸 상속이라고 한다.
class Car:
Manufacture = "India"
def __init__(self, color='red', category='sedan'):
self.color = color
self.category = category
def drive(self):
print("I'm driving")
def accel(self, speed_up, current_speed=10):
self.speed_up = speed_up
self.current_speed = current_speed + self.speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
Car
클래스의 기능은 유지한 채 maker
속성만 추가된 새로운 클래스 NewCar
를 선언하고 싶다면, 클래스의 상속 기능을 이용하면 된다.
class NewCar(Car):
pass
car = NewCar()
car.drive()
car.accel(10)
/---------------/
# 출력
I'm driving
speed up 10 driving at 20
Car
클래스와 비슷한 기능과 속성을 유지한 채 maker
속성만 추가해 조금 다른 클래스를 만들고 싶다면, Car
클래스를 상속받은 뒤 maker
속성만 추가해 주면 된다.
class NewCar(Car):
maker = 'Porsche'
car = NewCar()
car.maker
/-------------------/
# 출력
'Porsche'
자식 클래스, 부모 클래스
- 상속받은 클래스를 "자식 클래스", "서브 클래스(sub class)", "파생된 클래스(derived class)"라고 한다.
- 기존 클래스를 "부모 클래스", "슈퍼 클래스(super class)", "베이스 클래스(base class)"라고 한다.
super()
)1) 메서드 추가하기
자식 클래스에 새로운 메서드를 추가할 수 있다. 아래와 같이 정의하면 기존 Car
클래스의 메서드들과 함께 새로운 메서드 fly()
도 사용 가능하다.
class NewCar(Car):
def fly(self):
print("I'm flying!! This is the new car!!")
2) 메서드 오버라이드
자식 클래스의 drive() 메서드에 "I'm driving and can fly"라는 문구가 출력되는 메서드를 정의하자. 이렇게 기존에 있는 메서드를 변경하는 것을 메서드 오버라이드(재정의, override)라고 한다.
class NewCar(Car):
def fly(self):
print("I'm flying!! This is the new car!!")
def drive(self):
print("I'm driving and can fly")
fly()
: 메서드 추가drive()
: 메서드 재정의메소드 추가와 메소드 오버라이드의 차이는?
상속받은 클래스에서 없는 메소드를 정의 하는 것을 메소드 추가라고 하고 상속받은 클래스에서 있는 메소드를 다시 정의하는 것을 메소드 오버라이드라고 한다.
3) 부모 메서드 호출하기 super()
부모 메서드 호출은 super()
라는 함수를 이용한다. super()
는 파이썬 내장함수이다. 자식 클래스에서 부모 클래스의 메서드를 호출하고 싶을 때 사용한다.
super()
함수는 다음과 같이 사용한다.
def (부모클래스의)메서드이름():
super().메서드이름()
Car
클래스를 상속받은 NewCar
클래스의 초기화 함수(생성자) __init__()
메서드에 color
, category
속성은 동일하게 유지한 채 maker
속성을 추가한 NewCar
클래스를 만들어보자.
class NewCar(Car):
def __init__(self, color, category, maker):
super().__init__(color, category)
self.maker = maker
def fly(self):
print("I'm flying!! This is the new car!!")
def accel(self, speed_up, level=1, current_speed=10):
self.boost[level] = {1 : 0, 2 : 30, 3 : 50}
self.speed_up = speed_up + self.boost[level]
self.current_speed = current_speed + self.speed_up
print("speed up", self.speed_up, "driving at", self.current_speed)
왜 아래와 같이 정의하지 않았을까?
class NewCar(Car):
def __init__(self, color, category, maker):
self.color = color
self.category = category
self.maker = maker
지금은 부모 클래스에서 color
를 이름으로 받지만 color
를 RGB 값으로 받도록 바꾸고 싶다고 할 때, 만약 메서드를 오버라이드 했다면 상속받은 클래스 하나하나의 초기화 코드를 변경해 주어야 한다. 이렇게 super()
를 사용해 변수를 초기화하면 부모 클래스만 변경하면 된다.
즉, 부모 클래스의 변경사항이 그대로 자식 클래스에 반영된다.
class Car:
Manufacture = "India"
def __init__(self, color='red', category='sedan'):
self.color = color
self.category = '2020Y '+ category
class NewCar(Car):
def __init__(self, color, category, maker):
super().__init__(color, category)
self.maker = maker
newcar = NewCar('red','sports car', 'Kia')
print(newcar.category)
/----------------------------------------------------/
# 출력
2020Y sports car