1. 클래스가 무엇인지 이해하기

우리가 자동차를 만든다고 가정해보자. 자동차를 만들기 위해서, 우선 엑셀을 밟을 때 앞으로 나아가는 기능, 브레이크를 밟았을 때 멈추는 기능, 후진 기능 부터 자동차 내 공기를 순환시켜주는 기능, 라디오 기능, 네비게이션 기능 등등 여러 개의 기능을 분할하여 만들 것이다. 이와 같이 각 기능부분을 구성하는 코드를 모듈이라고 한다. "절차지향프로그래밍"언어에서는 프로그램을 이렇게 여러 기능으로 나누고 이를 모듈로 편성하여 프로그램을 작성하는데, 이 경우 각 모듈이 처리하는 "데이터"를 전혀 고려하지 않게 된다. 그래서 하나의 자동차를 만들 때 시간이 비슷하게 걸릴 수는 있어도, 여러 대의 자동차를 만들어 낼 때 각 자동차들마다 필요한 물건들을 따로따로 생성해야 하니 시간이 압도적으로 오래 걸리게 된다.

이런 문제를 해결하기 위해, "객체지향프로그래밍"언어에서는 데이터(객체)에 집중하여 같은 물건을 공장처럼 찍어낼 수 있도록 클래스(class)라는 개념을 만들었다. 이 클래스 안에는 자동차이기 위한 기능 뿐만 아니라, 자동차를 만들어내는데 필요한 부품들, 핸들, 바퀴, 프레임 등과 자동차 모델명, 최고속도, 연비와 같은 자동차에 대한 정보들도 같이 만들어낼 수 있다. 다시 말해서, 자료와 함수를 하나로 묶은 것을 클래스(class)라고 하고, 클래스에서 생성되는 것을 객체(object)라고 한다. 자동차 공장은 자동차를 만들어 내기만 할 뿐, 실제로 실행되는 것은 클래스가 아닌 객체이다. 객체는 특정 기능을 수행하는 함수(메소드, method)와 그에 필요한 데이터(변수)를 모두 가지는데 밖에서는 볼 수 없기 때문에 이를 캡슐화(encapsulation)했다 라고 표현한다. 그렇기 때문에 각각의 객체들은 다른 객체의 데이터를 함부로 변경시킬 수 없고, 만약 문제가 발생했다고 하면 문제가 발생한 해당 자동차만 리콜하여 수리하면 된다. (물론 필요하다면 다른 객체와 상호작용할 수도 있다.)

 

2. 클래스 선언하기: class

앞서 자동차에 비유를 했으므로, 자동차 클래스를 정의해보자.

우선 아무 것도 없는 자동차 클래스를 만들었다. 이처럼 클래스를 만들려면 class 키워드를 사용하여 정의해주면 된다. 위 자동차 클래스는 현재 빈 클래스 상태이며, 이제 자동차를 만들때 필요한 기능과 데이터들을 붙여줄 것이다. 파이썬에서 변수를 사용할 때 변수를 먼저 정의하고 초기화 해서 사용하는 것처럼, 우리도 Car 클래스를 정의했으니 이제 초기화 해줄 차례이다.

클래스를 초기화 할 때, 파이썬 객체 초기화 메소드 __init__ 를 사용한다. __init__ 는 클래스의 정의로부터 객체를 초기화하고, 이 메소드의 첫번째 인자인 self는 만들어진 객체 자기 자신을 가리킨다. 이 self 인자는 다음에 오는 다른 모든 인자들에게 self를 부여하는 효과를 가지며, 여기서 만들어지는 메소드는 이 클래스에 속한 것이라 선언하는 효과가 있다. (고유 id를 생성하는 효과)

만들어진 객체안의 속성들을 호출할 때는 '객체이름.(dot)속성이름'으로 호출해 주면 된다.

이렇게 만들어진 클래스가 어떻게 동작하는지 살펴보자.

  1. Car() 클래스의 정의를 찾는다.
  2. 새 객체를 메모리에 초기화한다.
  3. 객체의 __init__ 메서드를 호출한다. 새롭게 생성된 객체를 self에 전달하고, 각 인자들을 순서대로 전달한다.
  4. 객체에 전달한 인자들을 새로운 변수에 저장한다.
  5. 이렇게 만들어진 객체를 반환하여 hyundai에 초기화한다.

 

객체와 인스턴스 (object and instance)

인스턴스(instance)는 클래스에 의해 생성되는 객체로 각 인스턴스는 고유의 값을 가진다. 인스턴스라는 말은 특정 객체가 어떤 클래스의 객체인지를 관계위주로 설명할 때 주로 사용된다. 위의 예제에서 Car 클래스에 의해 만들어진 "객체 hyundai"는 "Car 클래스의 인스턴스"이다.

 

메소드 만들기 (creating method)

메소드는 클래스 안에 내장되어 있는 함수를 말한다. 자동차가 굴러가기 위해서 필수적으로 엑셀과 브레이크, 기어변속, 와이퍼 등의 기능들을 만들어야 한다. 앞서 __init__ 메소드를 통해 자동차를 만들때 필요한 부품들을 만들었다면, 이 부품들을 가지고 자동차를 굴러가게 만드는 함수들을 만드는 것이 다른 메소드를 정의하는 것이다. 이 메소드들 또한 첫번째 인자로 self를 받으며, 이때 self의 의미는 이 메소드는 메소드가 정의된 클래스 안에서 선언되며, 해당 클래스로 만들어진 객체만이 이 메소드를 쓸 수 있다고 선언하는 것이다.

 

3. 상속: Inheritance

자동차에는 여러가지 종류가 있다. 경차, 세단, 쿠페, 왜건, SUV, 리무진, 밴, 픽업트럭 같은 4륜구동 자동차가 있고, 자동차와는 조금 다르지만 카고트럭, 덤프, 트레일러 같은 화물차와 스쿠터, 오토바이 등의 원동기도 있다. 대부분의 차들은 비슷한 기능을 가지고 있지만 모든 자동차들이 같은 기능을 가지는 것은 아니다. 물론 기존 자동차 클래스를 가지고 어떤 기능은 잘라내고, 어떤기능은 추가하면서 새 클래스를 만들고 새 코드에 병합할 수도 있겠지만, 비슷한 기능을 수행하는 기존 클래스와 새로운 클래스가 서로 다른 위치에 있기 때문에 코드 관리가 힘들어 진다. 그래서 비슷한 기능을 하는 클래스를 상속 받아 일부를 추가하거나 변경하여 기존의 코드를 재사용하는 방법을 사용한다. 예를들어, 세단이라는 클래스를 만들 때, 자동차 클래스를 상속받아 자동차로서의 기능을 넣어준 뒤, 세단이 가져야할 특별한 기능을 추가한다. 이럴 경우 자동차 클래스를 부모 클래스, 세단 클래스를 자식 클래스 라고 한다.

 

4. 오버라이드, 재정의: Override

같은 자동차라고 해서 특정 기능을 똑같이 수행하는 것이 아닐 때도 있다. 예를들어 휘발유를 연료로 하는 자동차 클래스를 만들었다고 가정해보자. 이후 전기자동차라는 새로운 자동차가 나왔고, 기존의 자동차와 비슷한 기능을 수행하기에 전기자동차에 자동차 클래스를 상속받았다. 하지만 기존의 자동차는 휘발유를 연료로 하는 자동차이기 때문에 전기자동차에 맞게 돌아갈리가 없다. 그래서 우리는 휘발유 자동차를 위한 메소드를 전기 자동차를 위한 메소드로 재정의 해주어야 한다.

1. 자동차 클래스 만들기

2. 전기자동차 클래스를 만들고 자동차 클래스를 "상속"만 했을 경우

3. 전기자동차 클래스에 자동차 클래스를 상속받고 "오버라이딩"까지 한 경우

 

override (재정의) vs overload (중복정의)

Override는 부모 클래스에서 정의한 메소드를 자식 클래스에서 재정의 하는 것으로, 부모 클래스에서 정의한 함수를 자식 클래스에서 사용하기 위해 같은 이름의 함수를 정의하는 것 이다. Overload는 같은 클래스 내에서 같은 이름의 메소드가 중복된 것을 말한다. 같은 이름의 메소드가 중복되도 계속 사용하려면 (Overload 되려면) 메소드들끼리 매개변수 유형이나 갯수를 다르게 하여 동일한 메소드명을 가지고 있더라도 다양한 호출에 응답 가능하게 하면 된다.

사실 이전 예제는 메소드 오버라이딩과 생성자 오버로딩의 예제이다. 먼저 E_car클래스는 Car클래스를 상속받았기 때문에, Car클래스에서 초기화된 속성들을 그대로 사용할 수 있다. gasoline_car = Car("gasoline") 코드를 보면 gasoline_car 객체는 Car 클래스에 인자를 하나 전달하여 초기화 하고 있다. 하지만 electronic_car = E_car() 코드를 보면 electronic_car 객체는 E_car 클래스에 인자를 전달하고 있지 않다. 분명 Car 클래스를 상속했기 때문에, 객체를 생성하는데 필요한 매개변수는 1개인데 말이다. 그 이유는 Car 클래스를 생성할 때 default 값을 설정하여 자동으로 None 값이 들어갔기 때문이다.

 

5. 부모클래스에게 도움 받기: Super

앞서 전기자동차는 휘발유가 필요하지 않다는 메세지를 메소드 오버라이딩(재정의)를 통해 전달해보았다. 휘발유 자동차에는 휘발유가 필요하다 메세지처럼 전기자동차도 전기자동차에는 휘발유는 필요하지 않다는 메세지를 전달하면서 전기자동차에는 전기가 필요하다는 메세지 또한 보내보고 싶다. 마침 Car 클래스의 display 메소드에는 자동차에 어떤 연료가 들어가야지 작동한다는 메세지를 출력하주는 메소드가 있어, 이를 이용하고 싶다면 super() 메소드를 사용한다. super() 메소드를 통해 부모클래스를 참조하고 싶을 때 사용한다.

E_car 클래스를 초기화 할 때, super() 메소드를 통해서 Car 클래스가 정의한 속성들을 E_car에서도 동일하게 정의한다. electronic_car을 통해 E_car의 인스턴스를 생성할 때, Car 클래스와 중복되는 속성인 model과 fuel는 super() 메소드를 통해 Car 클래스를 참조하여 만들었고, 추가적으로 price 속성을 만들어 초기화했다. display() 메소드를 실행할 때는 우선 super() 메소드를 통해 Car 클래스의 display() 메소드를 먼저 실행하였고, 그 뒤에 추가적인 정보를 담은 메세지를 출력하였다.

 

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기