본문 바로가기

Programing/Python

[Python 입문] 13. 파이썬 클래스 01

목차

  • 클래스(Class)는 왜 필요할까?
  • 클래스 개념 잡기
  • 인스턴스(객체)란?
  • 이야기 형식으로 클래스 기초 쌓기 (self, __init__)

파이썬 클래스(Class)

1. 클래스(Class)는 왜 필요할까?

 지금 배우고 있는 파이썬을 포함해 다양한 프로그래밍 언어는 말 그대로 언어일 뿐, 근본적인 개념은 모두 비슷하다. 지금부터 배울 클래스(Class)는 이 개념이 존재하지 않는 프로그래밍 언어와 존재하는 프로그래밍 언어로 나누어진다. 예를 들어 프로그래밍 언어 중 하나인 C언어에는 클래스가 없다. 이 말은 굳이 클래스 없어도 어떤 프로그램이든지 만들 수 있다는 뜻이다.

 즉, 클래스(Class)는 앞서 공부한 함수나 자료형과 같이 프로그램 만들기 위해 꼭 필요한 요소가 아니다. 순수하게 편리성과 가독성에 의해 만들어진 기능이라고 볼 수 있다.

 

 

2. 클래스 개념 잡기

 클래스(Class)는 우리가 어린 시절 많이 했던 뽑기의 틀과 비슷하다. 별 모양의 틀(클래스)로 찍으면 별 모양의 뽑기(인스턴스)가 생성되는 것이다. 클래스란 어떠한 똑같은 것을 계속해서 만들어낼 수 있는 설계 도면과 같은 것이고, 인스턴스(객체)란 클래스에 의해 만들어진 피조물을 뜻한다.

class Simple:
	pass

 위의 소스코드는 파이썬 클래스의 가장 기본 예로 들 수 있지만 아직은 아무런 기능도 갖고 있지 않은 껍질뿐인 클래스이다. 하지만 이렇게 껍질뿐인 클래스도 인스턴스(객체)를 생성하는 기능은 가지고 있다.

 

 1개의 클래스는 무수히 많은 객체를 만들어 낼 수 있다. 위에서 만든 Simple 클래스의 객체를 만드는 방법은 아래와 같다.

a = Simple()
b = Simple()

 Simple()의 결과값을 돌려받는 a나 b가 바로 인스턴스(객체)다.

 

 

3. 인스턴스(객체)란?

 앞서 계속해서 인스턴스(객체)라는 말이 계속해서 나온다. 클래스를 이해하기 전에 인스턴스(객체)라는 개념을 알 필요가 있다. 먼저, 인스턴스(Instance)와 객체는 같은 말이라고 할 수 있다. 클래스에 의해 생성된 객체를 인스턴스라고 부른다.

 이런 객체와 인스턴스의 차이를 navi = Cat()로 예를 들면 navi는 객체다. 그리고 navi라는 객체는 Cat의 인스턴스다. 즉, 인스턴스란 특정 객체(navi)가 어떤 클래스(Cat)의 객체인지를 관계 위주로 설명할 때 사용한다. 따라서 navi는 객체이면서 Cat의 인스턴스라고 표현할 수 있다. (햇갈린다...)

c언어와 파이썬의 예시

 그리고 파이썬은 C언어와는 다르게,  모든 정보가 객체인 객체 지향 언어이다. 지금까지 사용했던 변수와 같은 데이터나 함수와 같은 코드가 모두 객체로 취급하며 하나의 고유값이며 공간이다.

 

 

4. 이야기 형식으로 클래스 기초 쌓기

4.1. 클래스 변수

>>> class Service:
...     secret = "영구는 배꼽이 두 개다."   <---- 클래스 변수
... 
>>> pey = Service()   <---- pey라는 아이디(인스턴스)로 Service 클래스 사용(가입)
>>> pey.secret   <---- 클래스 변수 호출 1
'영구는 배꼽이 두 개다.'
>>> Service.secret
'영구는 배꼽이 두 개다.'   <---- 클래스 변수 호출 2

 위에서 작성한 클래스의 이름은 Service다. 이 클래스를 사용하기 위해서 pey라는 아이디(인스턴스)로 클래스 Service를 불러온다. 그리고 클래스의 변수를 얻기 위해서는 아이디(인스턴스) pey에 도트 연산자(.)으로 secret 변수를 호출하면 된다. 이 방법 외에도 클래스 내외부에서 "클래스명. 변수명"으로도 클래스 변수를 호출할 수 있다. 일종의 클래스 전역 변수라고 할 수 있다. (클래스 밖에서도 일반 변수처럼 호출할 수 있다.)

 이때 작성한 클래스 변수는 해당 클래스를 사용하는 모두에게 공용으로 사용되는 변수다.

 

4.2. 클래스 함수

 이번에는 Service라는 클래스에 두 숫자를 더하는 서비스를 추가하면 아래와 같이 클래스가 수정된다. (지금부터는 소스코드의 길이가 길어지기 때문에 에디터를 사용한다.)

class Service:
    secret = "영구는 배꼽이 두 개다."   <---- 클래스 변수
    def sum(self, a, b):   <---- 더하기 서비스
        result = a + b
        print("{0} plus {1} is {2}." .format(a, b, result))   <---- 더하기 결과 출력

pey = Service()   <---- pey라는 아이디(인스턴스)로 Service 클래스 사용(가입)

print(pey.sum(1, 1))   <---- 더하기 서비스 사용
1 plus 1 is 2.   <---- 더하기 결과 출력

 

4.3. self 란?

 위에 작성한 클래스를 보면 sum 함수는 첫 번째 입력값으로 처음 보는 self라는 것을 받고, 두 번째, 세 번째 입력값으로 더하기를 할 숫자를 받는다. 이것만 본다면 입력을 받는 입력 인수가 총 3개다. sum 함수는 첫 번째 입력값을 통해 해당 클래스를 사용하기 위해 가입한 사람인지 아닌지를 판단한다. 

 따라서 pey.sum(pey, 1, 1)과 같이 첫 번째 입력 인수로 pey라는 인스턴스를 넣게 되면 sum 함수는 해당 인스턴스가 가입되어 있는지를 확인한 후 더하기 서비스를 제공하는 것이다. 하지만 위에서 작성한 클래스를 보면 pey.sum(1, 1)로 작성되어 있지만 문제없이 결과값이 출력된 것을 확인할 수 있다.

 pey.sum(1, 1)이라는 호출이 발생할 때 self는 자동으로 사용했던 인스턴스(pey라는 인스턴스)로 바뀌게 된다. 그렇기 때문에 pey.sum(1, 1)이 가능한 것이다. 이 pey,sum(1, 1)은 Service.sum(pey, 1, 1)과 같은 결과를 얻는다.)

 self라는 변수를 클래스 함수의 첫 번째 인수로 받아야 한다는 것은 파이썬만의 특징이다.

 

4.4. self 제대로 알기

 앞에서 추가한 더하기 서비스를 제공할 때 "ㅇㅇㅇ님 1 + 1 = 2 입니다."와 같이 사용자에게 이름을 입력받아 함께 출력될 수 있도록 해보자.

class Service:
    secret = "영구는 배꼽이 두 개다."
    def setname(self, name):
        self.name = name
    def sum(self, a, b):
        result = a + b
        print("{0},{1} plus {2} is {3}." .format(self.name, a, b, result))
        
pey = Service()   <---- 1.pey라는 아이디(인스턴스)로 Service 클래스 사용(가입)
pey.setname("Henry")   <---- 2.사용자 이름 입력
print(pey.sum(1, 1))   <---- 3.더하기 서비스 사용
Henry,1 plus 1 is 2.   <---- 4.더하기 결과 출력

 위의 클래스를 보면 먼저 Service 클래스 사용을 위해 pey라는 인스턴스를 얻는다. 그리고 pey라는 인스턴스를 가진 사람의 이름이 Henry라고 입력한다. 마지막으로 더하기 서비스를 사용하면 마지막과 같이 결과값이 출력된다. 위와 같이 pey라는 인스턴스와 Henry라는 이름을 연결해 주는 것이 바로 self다.

 setname 함수가 실행되는 순서는 아래와 같으니 참고하자.

# 1. pey라는 아이디(인스턴스)를 가진 사람이 "Henry"라는 이름을 setname 함수에 입력
pey.setname("Henry")

# 2. 다음 문장 수행
self.name = name

# 3. self는 첫 번째 입력값으로 pey라는 아이디를 받기 때문에 아래와 같이 인식
pey.name = name

# 4. name은 두 번째로 입력받은 "Henry"라는 값이기 때문에 다시 한 번 아래와 같이 바꿔서 인식
pey.name = "Henry"

 위에 설명한 내용을 토대로 기억해야 할 사항은 'sum 함수가 어떻게 pey라는 인스턴스를 가진 사람의 이름을 알아내게 되었을까'이다. 위 내용을 보면 pey.name = "Henry"라는 값을 갖게 된다는 사실을 확인했다. 따라서 sum 함수도 self.name은 pey.name로 치환된다. 따라서 sum 함수는 pey라는 인스턴스를 가진 사람의 이름을 알게 되는 것이다. (self는 Service에 의해 생성된 인스턴스를 지칭한다는 사실을 잊지 말자.)

 

4.5. __init__ 란?

 앞에서 추가된 사용자 이름 출력이 헷갈려서 아래와 같이 pey.setname("Henry")의 과정을 빼먹어서 오류가 발생하는 경우가 많이 있다.

pey = Service()
pey.sum(1, 1)

 

 이때 아래와 같이 __init__라는 함수를 사용하는 방법을 통해 pey.setname("Henry")와 같은 과정을 생략할 수 있다.

class Service:
    secret = "영구는 배꼽이 두 개다."
    def __init__(self, name):
        self.name = name
    def sum(self, a, b):
        result = a + b
        print("{0},{1} plus {2} is {3}." .format(self.name, a, b, result))

pey = Service("Henry")   <---- 1.pey라는 아이디(인스턴스)의 사용자는 Henry
print(pey.sum(1, 1))

 기존 클래스와 비교하면 setname 함수의 이름이 __init__로 바뀐 것 외에는 동일하다. 지금 사용한 __init__ 함수는 클래스에서 "인스턴스를 만들 때 항상 실행된다."라는 특별한 의미를 갖는다.

 즉, 아이디(인스턴스)를 부여받을 때마다 실행된다는 뜻이다. 따라서 아이디(인스턴스)를 부여받을 때 다음과 같이 입력하면 된다. (기존의 방법과 비교해보자.)

# 기존 사용 방식
pey = Service()
pey.setname("Henry")

# __init__ 적용 후 사용 방식
pey = Service("Henry")

 __init__ 함수를 사용했기 때문에 pey = Service("Henry")와 같이 아이디(인스턴스)를 부여받을 때 이름까지 함께 입력하면 된다.

 

 위 내용들을 통해 인스턴스, self, __init__ 함수의 의미에 대해 조금 쉽게 접근할 수 있었다. 앞선 설명에서 pey를 아이디로 불렀지만 이것이 인스턴스라고 불린다는 것을 꼭 기억하고, 아래 클래스의 기본적인 구조를 살펴보자.

class 클래스 이름(상속 클래스 명):
	<클래스 변수1>
    <클래스 변수2>
    ...
    def 클래스 함수1(self, 인수1, 인수2, ...)
    	<수행할 문장1>
        <수행할 문장2>
        ...
	def 클래스 함수2(self, 인수1, 인수2, ...)
    	<수행할 문장1>
        <수행할 문장2>
        ...
	def 클래스 함수N(self, 인수1, 인수2, ...)
    	<수행할 문장1>
        <수행할 문장2>
        ...
    ...
        

 클래스 내부에서 선언할 수 있는 클래스 변수와 함수의 개수는 제한이 없다.

 class라는 키워드는 클래스를 만들 때 사용하는 예약어라고 할 수 있고, 바로 뒤에 클래스의 이름을 입력한다. 만약 상속할 클래스가 있다면 클래스 이름 뒤에 괄호() 안에 상속할 클래스의 이름을 입력하면 된다. (클래스의 상속은 뒤에 설명이 나오니 그때 공부하자.)

 

 

내용 출처: 책 '점프 투 파이썬'의 내용