클래스는 지금까지 공부한 함수나 자료형처럼 프로그램 작성을 위해 꼭 필요한 요소는 아니다.
하지만 프로그램을 작성할 때 클래스를 적재적소에 사용하면 프로그래머가 얻을 수 있는 이익은 많다. 예제를 통해 한번 생각해 보자.
클래스가 유용하게 쓰이는 예시는 다음과 같다.
일반적으로 계산기의 기능에 대해서 생각해볼 때, 계산기는 이전에 계산한 결과값에서 추가적으로 계산을 이어나갈 수 있다. 즉, 메모리기능이 있다는 것인데,
클래스를 이용하지 않고, 함수로 이를 구현해보면 아래와 같다.
# class.py
result = 0
def add(num):
global result
result += num
return result
print(add(3))
print(add(4))
Python
복사
결과는 3, 7 이 차례로 나온다.
여기서 핵심은 이전의 값을 그대로 이용하기 위해서 global 함수를 적용해 result 를 전역변수로 정의했다는 점이다.
이를 응용해서, 빼기 기능을 함께 이용하고 싶다고 한다면 아래와 같이 함수를 추가해줘야 한다.
result1 = 0
result2 = 0
def add(num):
global result1
result1 += num
return result1
def sub(num):
global result2
result2 -= num
return result2
print(add(3))
print(add(4))
print(sub(3))
print(sub(7))
Python
복사
하지만, 계산이 점점 복잡해질수록 함수를 계속해서 추가하게 된다면, 비효율적인 코드가 될 수 밖에 없다.
이를 해결하기 위해 쓰이는 것이 class 가 된 것이다.
대략적인 쓰임새는 아래와 같다.
# calculator3.py
class Calculator:
def __init__(self): # 생성자
self.result = 0
def add(self, num):
self.result += num
return self.result
cal1 = Calculator() # 객체1
cal2 = Calculator() # 객체2
print(cal1.add(3))
print(cal1.add(4))
print(cal2.add(3))
print(cal2.add(7))
Python
복사
위의 코드에서 cal1, cal2는 ‘객체’ 인데, 이 객체가 바로 독립적인 역할을 해주며, 계산기가 늘어나도 객체만 생성해주면 되기 때문에 앞의 함수를 이용했을 때보다 더 간단하게 구현할 수 있는 것이다.
따라서, 빼기 기능을 수행하는 계산기를 추가해준다고 하면,
class Calculator:
def __init__(self):
self.result = 0
def add(self, num):
self.result += num
return self.result
def sub(self, num): # 빼기 함수 추가
self.result -= num
return self.result
Python
복사
클래스와 객체
다음의 그림이 클래스와 객체의 역할을 잘 설명해준다.
클래스로 만든 객체에는 중요한 특징이 있다. 바로 객체마다 고유한 성격을 가진다는 것이다.
과자 틀로 만든 과자에 구멍을 뚫거나 조금 베어 먹더라도 다른 과자에는 아무런 영향이 없는 것과 마찬가지로 동일한 클래스로 만든 객체들은 서로 전혀 영향을 주지 않는다. (즉, 앞서 만든 객체 cal1, cal2는 서로 아무런 영향을 끼치지 않고 독립적으로 사용되는 것)
다음은 파이썬 클래스의 가장 간단한 예시다.
class Cookie:
pass
Python
복사
Cookie 클래스는 아무런 기능도 가지고 있지 않다.
하지만, 이런 껍질뿐만인 클래스도, 결국 클래스이기 때문에 객체를 생성할 수 있다.
a = Cookie()
b = Cookie()
Python
복사
클래스로 만든 객체를 ‘인스턴스’ 라고 부르기도 한다.
’인스턴스’ 와 ‘객체’의 차이는 다음과 같다.
a = Cookie()로 만든 a는 객체이다. 그리고 a 객체는 Cookie의 인스턴스이다. 즉, 인스턴스라는 말은 특정 객체(a)가 어떤 클래스(Cookie)의 객체인지를 관계 위주로 설명할 때 사용한다.
종합해보면, ‘a는 인스턴스’보다 ‘a는 객체’라는 표현이 어울리며 ‘a는 Cookie의 객체’보다 ‘a는 Cookie의 인스턴스’라는 표현을 사용한다.
클래스 내용 구상하기
클래스를 만들 때는 항상 클래스로 만든 객체가 어떻게 작동할지를 생각해서 차근차근 만들어나가는 것이 중요하다.
그럼 다음과 같이 거꾸로 올라가면서 생각해볼 수 있다.
a = FourCal() # a 라는 클래스 객체를 생성할 것이고
Python
복사
a.setdata(3,4) # setdata 라는 메서드 안에 파라미터를 입력해줄 것이다.
Python
복사
a.add() # 덧셈을 수행할 것이고
a.sub() # 뺄셈을 수행할 것이고
a.div() # 나눗셈을 수행할 것이고
a.mul() # 곱셈을 수행할 것이다
Python
복사
위의 수행 순서를 생각해서 클래스를 구상해보자.
class FourCal:
# 연산을 수행할 수 있게 파라미터를 받는 메서드를 정의
def setdata(self, first, second):
self.first = first
self.second = second
Python
복사
이때, 일반 함수와 달리 메서드의 첫 번째 매개변수 self는 특별한 의미를 가진다.
클래스 객체를 생성하고, 메서드를 사용할 때
a = FourCal()
a.setdata(4,2)
Python
복사
다음과 같이 self를 제외한 2개의 매개변수 값만을 전달하면 된다.
그 이유는 setdata 메서드의 첫 번째 매개변수 self에는 setdata 메서드를 호출한 객체 a가 자동으로 전달되기 때문이다.
파이썬 메서드의 첫 번째 매개변수 이름은 관례적으로 self를 사용한다. 물론 self 말고 다른 이름을 사용해도 상관없다.
이제 위의 a.setdata(4,2) 가 수행됐을 때, 다음과 같이 생각할 수 있다.
# 객체 변수 or 속성
a.first = 4
a.second = 2
Python
복사
이때 first와 second 는 객체 a 의 객체 변수(또는 속성)이 된다.
a = FourCal()
b = FourCal()
a.setdata(4,2)
print(a.first)
b.setdata(5,6)
print(b.first)
Python
복사
결과는 4, 5가 나온다.
이제, 더하기 기능을 구현하는 함수를 또 만들어줘야 한다.
그 전에 class 내 더하기 method 를 구현하고자 한다면 다음과 같이 쿼리를 날릴 것이다.
a = FourCal() # 클래스 객체 생성
a.setdata(4,2) # setdata 메소드 실행 -> 클래스 객체 변수 생성
a.add() # 덧셈 메소드 실행
Python
복사
위의 코드 순서를 고려해서 클래스 내용을 수정해보면
class FourCal:
# 연산을 수행할 수 있게 파라미터를 받는 메서드를 정의
def setdata(self, first, second):
self.first = first
self.second = second
# 더하기 메서드 정의
def add(self):
result = self.first + self.second
return result
a = FourCal()
a.setdata(4,2)
a.add()
Python
복사
결과는 6이 나온다.
이제 덧셈, 뺄셈, 곱셈, 나눗셈의 기능을 모두 가진 계산기 클래스를 최종적으로 만들면,
# calculator.py
class FourCal:
def setdata(self, first, second):
self.first = first
self.second = second
def add(self):
result = self.first + self.second
return result
def sub(self):
result = self.first - self.second
return result
def mul(self):
result = self.first * self.second
return result
def div(self):
result = self.first / self.second
return result
a = FourCal()
b = FourCal()
a.setdata(4,2)
b.setdata(3,8)
a.add()
a.sub()
a.mul()
a.div()
b.add()
b.sub()
b.mul()
b.div()
Python
복사
생성자
만약 다음과 같이 코드를 작성하면 어떻게 될까?
>>> a = FourCal()
>>> a.add()
Python
복사
위 코드를 실행시키면 아래와 같은 에러가 발생한다.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in add
AttributeError: 'FourCal' object has no attribute 'first'
오류의 원인은
FourCal 클래스의 인스턴스 a에 setdata 메서드를 수행하지 않고 add 메서드를 먼저 수행하면 ‘AttributeError: 'FourCal' object has no attribute 'first'’오류가 발생한다. setdata 메서드를 수행해야 객체 a의 객체변수 first와 second가 생성되기 때문이다. (→ a.first, a.second)
이렇게 객체에 first, second와 같은 초깃값을 설정해야 할 필요가 있을 때는 setdata와 같은 메서드를 호출하여 초깃값을 설정하기보다 생성자를 구현하는 것이 안전한 방법이다.
생성자(constructor)란 객체가 생성될 때 자동으로 호출되는 메서드를 의미한다. 파이썬 메서드명으로 __init__를 사용하면 이 메서드는 생성자가 된다.
생성자를 FourCal 클래스에 추가해보자.
class FourCal:
def __init__(self,first,second):
self.first = first
self.second = second
def add(self):
result = self.first + self.second
return result
def sub(self):
result = self.first - self.second
return result
def mul(self):
result = self.first * self.second
return result
def div(self):
result = self.first / self.second
return result
Python
복사
사실상 __init__ 은 setdata 와 이름만 다를 뿐, 모든 코드가 같다.
하지만, 따로 setdata 메서드의 경우처럼 호출할 필요 없이, 클래스 객체를 정의해줌과 동시에 호출이 자동으로 이루어진다는 장점이 있다.
그렇다면, 다시 클래스 객체를 호출해보자
a = FourCal()
Python
복사
아래와 같은 오류가 발생한다.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: FourCal.init() missing 2 required positional arguments: 'first' and 'second'
→ init 생성자에서 2가지의 arguments 인 fist, second 가 없다고 없다고 한다.
즉, 생성자의 매개변수 first와 second에 해당하는 값이 전달되지 않았기 때문이다.
따라서, 다음과 같이 코드를 변경해줘야 한다.
a = FourCal(4,2)
Python
복사
결국 __init__ 생성자를 이용하지 않았을 때는 아래와 같이 2줄이었던 코드를
a = FourCal()
a.setdata(4,2)
Python
복사
생성자를 사용하면 다음과 같이 한줄의 코드로 줄일 수 있는 것이다.
a = FourCal(4,2)
Python
복사