파이썬(객체지향, 클래스)

LEEEEE·2022년 1월 10일
0

객체지향 프로그래밍

Everything in Python is an object, and almost everything has attributes and methods.

파이썬에서는 부울(bool), 정수, 실수, 문자열, 배열, 딕셔너리, 함수, 모듈, 프로그램 등 모든 것이 객체이다. 파이썬에서 object라 불리는 것들은 모두 변수에 할당될 수 있고, 함수의 인자로 넘겨질 수 있는 것들이다.

  • 파이썬에서는 모든 것(부울, 정수, 실수, 데이터구조(list,tuple,dict,set...), 함수, 프로그램, 모듈)이 객체다.
  • 객체는 상태(state)를 나타내는 속성(attribute)과 동작(behavior)을 나타내는 메서드(method)가 있다.
  • 객체의 속성은 변수로 구현된다. 객체의 메서드는 함수로 구현된다.

객체지향이란?

변수와 같은 객체를 담는 것을 활용한 프로그래밍을 객체 지향 프로그래밍(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

    그리고 추가로 클래스명은 주로 명사로, 함수명은 주로 동사로 명명한다.

클래스 속성과 메서드

클래스에서 속성과 메서드는 어떻게 구현할까?

  • 클래스의 속성은 상태(state)를 표현한다. 속성은 변수로 나타낸다.
  • 클래스의 메서드는 동작(behavior)을 표현한다. 메서드는 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의 값은 인터프리터가 제공한다.
  • 인스턴스 변수를 정의할 때에는 접두사 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__ 메서드 안에 정의된 속성(변수) colorcategory는 클래스를 인스턴스화할 때 값을 설정할 수 있다.
  • 이를 인스턴스 객체의 초기화 (initializing instance) 라고 히고, __init__함수는 생성자(constructor)라고 한다.
  • __init__ 역시 def 키워드로 정의한다. 즉, 클래스 안의 메서드이므로 self 문법 역시 사용해야한다.
car2 = Car2('yellow', 'sedan')
print(car2.color)
car2.category
/----------------------------/
# 출력
yellow
'sedan'

요약

  • __init__이라고 쓰고, "던더(Double Under) 이닛"이라고 발음
  • 다른 객체 지향 언어를 알고 있다면 생성자라는 말을 들으면 객체 인스턴스화와 초기화 2가지 작업을 생각할 수 있다.
  • 파이썬의 생성자는 초기화만 수행하고, 객체 인스턴스화는 클래스 사용 시 변수 할당을 통해 이루어진다.
  • __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 속성은 car1car2가 공유

인스턴스 변수

  • __init__() 안에서 self를 사용해 선언된 변수를 인스턴스 변수라고 함. 객체가 인스턴스화될 때마다 새로운 값이 할당되며 서로 다른 객체 간에는 값을 공유할 수 없음.
  • colorcategory는 인스턴스 변수
  • colorcategory 속성은 car1car2가 공유하지 않음.

즉! 객체 단위로 변경되는 변수는 인스턴스 변수로 선언


상속

부모 클래스가 자식 클래스에게 '멤버변수'와 '메서드'를 물려주는 걸 상속이라고 한다.

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)"라고 한다.

상속 사용하기

  • 메서드 추가하기(add)
  • 메서드 재정의하기(override)
  • 부모 메서드 호출하기(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

0개의 댓글