반응형

함수뿐만 아니라 다양한 변수를 포함한 큰 프로그램을 하나의 묶음으로 정의하는 것이 "클래스(class)"이다. 클래스의 기본적인 사용법부터 설명한다.


함수와 클래스

함수는 하나의 처리를 하나로 통합한 것이지만, 이런 함수가 많이 늘어나면, 점차적으로 어느 것이 무슨 역할를 하는지 의미를 알기 힘들어 진다. 예를 들어, 수백개의 함수가 나열되어 있으면, 그것을 전부 이해해 나가는 것은 힘들 것이다.

그래서 "비슷한 역할을 하는 것을 한곳에 모으자"라고 누구든지 생각된다.

예를 들어, 어떤 데이터 처리를 만드는 것을 생각해 보자. 데이터를 관리하는 함수, 데이터를 추가하는 함수, 데이터를 삭제하는 함수, 데이터를 출력하는 함수 ...... 따위가 쭉 늘어서 있는 것은 그다지 사용하기가 쉽지 않다.

그래서 "데이터를 처리하기 위해 필요한 것"을 모두 한 묶음으로 두자"라고 생각하게 된다. 큰 "데이터 관계 묶음'이라는 것을 만들고, 그 안에 "데이터를 보관할 변수", "데이터를 파일에 읽고 쓰는 함수", "데이터를 추가하거나 삭제하는 함수", "데이터를 출력하는 함수" ......와 같이, 그 데이터 처리에 필요한 변수와 함수를 모두 하나로 모으자는 것이다.

그렇게 되면 데이터의 처리에 관해서는 우선 "이 묶음에 안에 반드시 있다"라는 것이 되기 때문에, 곳곳의 함수를 찾지 않아도 된다.

이것이 "클래스(class)"의 개념이다. 클래스라는 것은 어떤 목적을 위해 필요한 '값'과 '처리'를 모두 한 묶음으로 한 것이다.

이 클래스는 아래와 같은 같은 형태로 만든다. "class 클래스 이름:"라는 것으로 시작하여 그 아래에 클래스가 제공하는 변수와 함수를 들여 쓰기하여 작성한다.

클래스의 정의

class 클래스 이름:
  변수1
  변수2
  ......필요한 만큼 변수를 제공......

  def 메소드1(인수):
     ......메소드의 처리......
   
  def 메소드2(인수):
     ......메소드의 처리......
   
  ......필요한만큼 메소드를 제공......

클래스에 필요한 값을 저장하는 변수를 "멤버 변수", 클래스에 제공하는 함수를 "메소드"라고 한다.

이러한 작성법은 기본적으로 일반 변수와 함수의 작성법 동일하다. 단지 "class OO:"라는 정의에 쓰면 멤버 변수와 메소드로 처리 할 수 있게 된다는 것 뿐이다. 특별한 작성법 등은 없다.


클래스 생성

이제 실제로 클래스를 만들어 사용해 보기로 하자. 이전에 "이름을 사용하여 메시지를 표시한다"는 것을 클래스에 해보기로 한다.

아래에 샘플을 예제를 보도록 하자. 여기에서는 "Member"라는 클래스를 만들었다.

class Member: 
    name = "" 
   
    def showMsg(self): 
        print("Hello, " + self.name + ". How are you?")

멤버 변수는 이름을 저장하는 "name"이라는 변수를 선언하였다. 메소드는 메시지를 표시하는 "showMsg"를 선언하고 있다.

이 클래스의 소스 코드를 보면, 여태 보지 못한 것이 등장하고 있다. "self"라는 것인다. 이것은 showMsg 인수에 사용되고 있다. "뭐야, 단지 인수에 사용한 변수가?"라고 생각하면 그렇지 않다는 것을 알 수 있다.

메소드 안에는 'self.name'라는 식으로 왠지 잘 모르는 사용 법을 사용하고 있다.

사실은 이 "self"라는 것은 "자신"을 나타내는 특별한 값이다. 자신은 클래스가 아니다. 클래스에서 만들어진 "인스턴스"라는 거다.

인스턴스와 self

클래스라는 것은 함수 등과 같이 그대로 클래스에서 메소드를 호출하거나 해서 사용하지는 않는다. 클래스를 이용하기 위해서는 "인스턴스"라는 것을 만들어야 한다.

클래스라는 것은 말하자면 프로그램의 "청사진"이다. 이것 자체를 조작하는 것이 아니라, 이 클래스는 설계도를 바탕으로 실제로 사용할 수 있는 부품을 만들어 그것을 조작하는 거다. 이 부품이 인스턴스이다.

만약 클래스를 그대로 사용하면, 그 클래스의 기능을 몇번이고 사용하고 싶다면 많은 클래스를 만들어야 한다. 예를 들어, 샘플 Member 클래스를 사용하여 "Taro"며 "Hanako"의 데이터를 관리하려 했다고 생각해 보자. 클래스를 그대로 사용하게 되면 그 name에 "Taro"로 설정하면, 또 "Hanako"는 보관할 수 없게되어 버린다.

그래서 클래스를 바탕으로 "인스턴스"라는 부품을 만들고, 그것에 Taro로 설정해 주어야 한다. Hanako가 필요하게 되면, 또한 클래스에서 새로운 인스턴스를 만들고, 거기에 Hanako라고 설정 해준다. 이런 상태로 "Member 클래스를 사용할 필요가 있으면 새로 Member의 인스턴스를 만들고, 이름을 설정해 주어야"입니다. 이렇게 하면 이 클래스를 바탕으로 얼마든지 데이터를 처리할 수 있게 된다.

그리고, 이 인스턴스 자신을 가리키는 데 준비하는 것이 "self"라는 것이다.

예를 들어, 어떤 메소드에서 "이 인스턴스에 저장되어 있는 멤버 변수의 값을 사용해야 한다"고 해보자. 이 예에서 말한다면, Member의 name 값을 showMsg에서 사용하는 경우이다.

이 때, 그냥 'name'변수 이름으로 사용할 수 없다. "이 인스턴스 안에 있는 name" 형태로 지정을 해주지 않으면 안된다.

그래서 Python 클래스에 제공되는 메소드는 반드시 첫 번째 인수에 인스턴스 자신을 나타내는 값을 전달하도록 해야 한다. 이것이 "self"의 정체는 것이다. 이 self 안에있는 멤버 변수와 메소드는

 self."변수"

따라서, self후에 점(.)으로 지정한다. 예를 들어, 여기에 "self.name"라고 하고 name 멤버 변수를 지정하고 이용하면 된다.


클래스 사용

이제 만든 Member 클래스를 사용해 보자. 아래에 사용할 소스 코드 예제가 있다.

class Member: 
    name = "" 
   
    def showMsg(self): 
        print("Hello," + self.name + ".How are you?") 
   
taro = Member() 
taro.name = "Taro"
taro.showMsg() 
   
hanako = Member() 
hanako.name = "Hanako"
hanako.showMsg()

여기에서는 Taro와 Hanako라는 2명의 데이터를 처리하기 위해 2개의 인스턴스를 만들어 사용하고 있다.

인스턴스의 생성은 "클래스 이름()"과 같이 이걸을 호출을 한다. 여기에서는

taro = Member()

이렇게 호출하고 있다. 이제 변수 taro에 Member 클래스의 인스턴스가 만들어 보관된다. 또한 인스턴스의 멤버 변수와 메소드는 점(.)을 사용하여 변수 이름 뒤에 해당되는 변수와 메소드를 작성해서 호출한다. 예를 들어,

taro.name = "Taro"
taro.showMsg()

이것은 taro 인스턴스의 name 멤버 변수에 "Taro"라고 값을 설정 해주고, 그러고 나서 showMsg 메소드를 호출해 실행을 한다. 이런 식으로 인스턴스를 만들어 변수에 할당 해두면 클래스 안에 있는 요소는 자유롭게 사용할 수 있다.

또한, 이 name와 같이 각 인스턴스에 값을 저장하고 사용하는 멤버 변수를 "인스턴스 변수"라고도 한다.

self는 어디 갔지?

그런데 이 사용 예를 보고 무언가 의문을 생기지 않나요? 그것은 showMsg를 호출하는 부분이다. "taro.showMsg()"라고 되어 있다.

어? showMsg는, 첫번째 인수에 "self"가 준비되어 있지 않은가? 그 self는 도대체 어떻게 된 것일까?

사실을 말하면, 메소드의 첫번째 인수로 전달되는 "인스턴스 자신"의 값은 Python의 시스템에 의해 자동으로 넘겨지게 된다. 즉, 첫번째 인수(self) 메서드를 호출할 때 불필요한 거다. 호출할 때, 두 번째 인수 이후만 작성한다. (여기에서는 첫번째 인수밖에 없기 때문에, 호출할 때 인수를 생략했다)

이런 식으로 인스턴스를 만들고 그 안의 멤버 변수를 설정하고 메서드를 호출한다. 이것이 클래스를 사용하는 기본이다. 이러한 기본 작업을 알면 클래스는 쉽게 사용할 수 있다.


생성자 사용

그렇게 하더라도,이 Member클래스 별로 유용하지 않는다. 인스턴스를 만들고, name을 설정하여 showMsg를 호출 ... 결국 하나 하나 다하고 있는 것은 클래스를 사용하지 않는 경우와 별로 차이가 없다. 게다가 인스턴스를 만든 후에 멤버 변수의 설정을 잊으면, 생각대로 움직이지 않게 되어 버린다. 적어도 "필요한 값은 처음부터 제대로 설정 사용"하도록 하고 싶다.

이럴 때에 유용한 것이 '생성자(constructor)'라는 것이 있다. 생성자는 "인스턴스를 만들 때 자동으로 호출되는 인스턴스 초기화를 위한 특별한 방법"이다. 이것은 다음과 같이 만든다.

def __init __ (self 인수 ...):
    ...... 초기화 처리 ......

생성자는 "__init__"와 같은 이름으로 메소드를 작성한다. 만약 어떤 값을 인수로 전달 싶다면, 2번째 인수 이후에 지정한다(첫번째 인수는 반드시 self이다).

이와 같이 생성자를 준비하면 인스턴스를 만들 때, 이 생성자를 사용하게 된다. 아래 예제를 보도록 하자.

class Member: 
   name = "" 
  
   def __init__(self, str): 
       self.name = str
  
   def showMsg(self): 
       print("Hello," + self.name + ".How are you?") 
  
taro = Member("Taro") 
taro.showMsg() 
  
hanako = Member("Hanako") 
hanako.showMsg()

여기에서는 다음과 같이 하여 str라는 인수를 전달하는 형태로 생성자를 제공하고 있다.

def __init__(self, str):

이제 인스턴스를 만들 때 name을 설정해야 한다. 실제로 인스턴스를 만들고 있는 곳을 보면,

taro = Member("Taro")

이런 식으로 ()에 이름을 인수로 넣고 있는 것을 볼 수 있다. 이렇게 해서 인수를 지정하여 인스턴스를 만들 수 있게 하면 필요한 멤버 변수의 설정도 한꺼번에 할 수 있어 매우 편리하다.



상속 클래스

클래스를 정의하는 가장 큰 장점은 "재사용이 가능하다"라는 것이다. 한번 만들면, 이후에는 그것을 그대로 복사하여 여기 저기에서 사용할 수 있다.

그러나 실제로 사용해 보면 "여기는 이렇게 하는 것이 더 좋다"라고 하는 것이 나올 거다. 하지만, 어느 정도의 규모의 프로그램가 되면, 곳곳에서 클래스를 사용하고 있으면 이런 수정은 제공되지 않는다.

이럴 때 정말 편리한 기능이 준비되어 있다. 그것은 "상속"이라는 것이다.

상속이라는 것은 이미 클래스를 그대로 이어 받아 새로운 클래스를 만드는 것이다. "이어 받는다"라는 것은 "클래스의 모든 기능을 그대로 이어 받는다"라는 것이다. 즉, 그 클래스에 있는 것을 통째로 그대로 받아 새로운 클래스를 만드는 거다.

이 상속을 사용하여 클래스를 정의하려면 다음과 같이 클래스를 만듭니다.

def 클래스 이름(상속하는 클래스):
    ...... 클래스의 내용 ......

상속에는, 상속하는 원래 클래스를 "기본 클래스", 새로 만든 클래스를 "파생 클래스"라고 한다.

실제 사용 예를 아래와 같다.

class Member: 
    name = "" 
   
    def __init__(self,str): 
        self.name = str
   
    def showMsg(self): 
        print("Hello," + self.name + ".How are you?") 
   
class PowerMember (Member): 
    mail = "" 
   
    def __init__(self,str1,str2): 
        self.name = str1 
        self.mail = str2 
   
    def showMsg(self): 
        print("Hello," + self.name + ".") 
        print("Your mail address is '" + self.mail + "'.") 
   
   
taro = Member("Taro") 
taro.showMsg() 
   
hanako = PowerMember("Hanako","hanako@flower.com") 
hanako.showMsg() 

여기에서는 Member 클래스를 상속하여 PowerMember라는 파생 클래스를 만들고 있다.

이 PowerMember 클래스는 def __init__(self, str1, str2):이렇게 하여, 2개의 인수를 준비했다. 그리고 self.name과 self.mail에 각각 설정하고 있다. 그런데, 이상한게 있다. 조금 자세히 더 살펴 보도록 하자.

잘 보면 이 PowerMember 클래스는 "mail"밖에 인스턴스 변수가 포함되어 있지 않았다. 그런데 self.name 값은 잘 저장하고 있다. 이것은 기본 클래스인 Member에 name가 준비되어 있기 때문이다. 상속은 기본 클래스의 모든 기능을 이어 받아 사용할 수 있다. 따라서 PowerMember에 name을 준비 할 필요는 없다.

멤버 변수뿐만 아니라 메소드도 모든 슈퍼 클래스에 있는 것은 그대로 사용할 수 있다. 이 상속을 사용하면 이미 있는 클래스를 점점 확장하여 기능 강화해 나갈 수 있다는 것이다.

Python에는 다양한 클래스가 라이브러리에 포함되어 있다. 그리고 그들을 이용할 때, 이 "상속"이 사용되고 있다.

예를 들어, 먼저 "리스트", "튜플", "레인지"라는 컨테이너에 대해 설명을 했었다. 이런 것도 실은 모든 "클래스"로 준비되어 있다. 이 3개의 클래스에 공통된 기능이 많은 것은 '시컨스'라는 인덱스에서 관리하는 컨테이너 클래스가 있고 그것을 계승하고 리스트나 튜플이 있다고 생각하면 이미지화하기 쉬울 것이다.

반응형

'Python' 카테고리의 다른 글

[Python] 클래스 사용  (0) 2017.12.22
[Python] 함수(function)  (0) 2017.12.22
[Python] 리스트, 튜플, 레인지, 세트, 사전  (0) 2017.12.22
[Python] 구문(statement)  (0) 2017.12.22
[Python] 우선 값과 계산의 기본  (0) 2017.12.22
[Python] Python 개발 환경  (0) 2017.12.22

+ Recent posts