
Python 클래스
클래스란?
클래스는 지금까지 공부한 함수나 자료형처럼 프로그램 작성을 위해 꼭 필요한 요소는 아니다.
하지만, 프로그램을 작성할 때 클래스를 적재적소에 사용하면 프로그래머가 얻을 수 있는 이익이 상당하다.
계산기를 생각해 보면, 계산기에 숫자 3을 입력하고 + 기호를 입력한 후 4를 입력하면 7을 보여 준다.
다시 한 번 + 기호를 입력한 후 3을 입력하면 기존 결괏값 7에 3을 더해 10을 보여 준다.
즉 계산기는 이전에 계산한 결괏값을 항상 메모리 어딘가에 저장하고 있어야 한다.
다음 '더하기'기능의 파이썬 코드를 살펴보자.
## 더하기 기능 예제
result = 0
def add(num):
global result
result += num
return result
...
print(add(3))
→ 3
print(add(4))
→ 7
이전에 계산한 결괏값을 유지하기 위해서 result 전역 변수(global)를 사용했다.
프로그램을 실행하면 예상한 대로 위와 같은 결괏값이 출력된다.
만약 한 프로그램에서 2대의 계산기가 필요한 상황이 발생하면 각각의 결괏값을 유지하기 위해 함수를 각각 따로 만들어야 한다.
다음 예제를 살펴보자.
## 2개함수 더하기 기능 예제
result1 = 0
result2 = 0
def add1(num):
global result1
result1 += num
return result1
...
def add2(num):
global result2
result2 += num
return result2
...
print(add1(3))
→ 3
print(add2(4))
→ 7
print(add2(3))
→ 3
print(add2(7))
→ 10
클래스와 객체
클래스란 똑같은 무엇인가를 계속해서 만들어 낼 수 있는 설계 도면이고, 객체란 클래스로 만든 피조물을 뜻한다.
클래스로 만든 객체에 중요한 특징이 있다. 바로 객체마다 고유한 성격을 가진다는 것이다.
동일한 클래스로 만든 객체들은 서로 전혀 영향을 주지 않는다. 아래 예제를 살펴보자.
## 클래스와 객체
class Cookie: ← 클래스(Class)
pass
...
a = Cookie() ← 객체(object)
b = Cookie() ← 객체(object)
사칙연산 클래스 만들기
사칙연산을 쉽게 해주는 클래스를 만들어 볼 것이다. 다음 순서에 따라 만들어 보자.
1. 클래스를 어떻게 만들지 먼저 구상하기
클래스는 무작정 만드는 것보다 클래스로 만든 객체를 중심으로 어떤 식으로 동작하게 할 것인지 미리 구상을 한 후에
생각한 것들을 하나씩 해결하면서 완성해 나가는 것이 좋다.
먼저, a = FourCal( )를 입력해서 a라는 객체를 만든다. 이후 a.setdata(4, 2)처럼 입력해서 숫자 4와 2를 a에 지정한다.
a.add( )는 두 수를 합, a.mul( )는 두 수의 곱, a.sub( )는 두 수의 차, a.div( )를 수행하면 두 수를 나눈 결괏값을 보여준다.
이렇게 동작하는 FourCal 클래스를 만드는 것이 목표이다.
2. 클래스 구조 만들기
앞에서 구상한 것처럼 동작하는 클래스를 만들어 보자.
우선 대화형 인터프리터에서 pass란 문장만을 포함한 FourCal 클래스를 만든다.
현재 상태에서 FourCal 클래스는 아무 변수나 함수도 포함하지 않지만 원하는 객체 a를 만들 수 있는 기능은 가지고 있다.
## 클래스 구조 만들기
class FourCal:
pass
...
a = FourCal()
type(a)
→ <class '__main__.FourCal'>
위와 같이 a = FourCal( )로 a 객체를 먼저 만들고 그 다음에 type(a)로 a 객체가 어떤 타입인지 알 수 있다.
3. 객체에 숫자 지정할 수 있게 만들기
생성된 객체 a는 아직 아무런 기능도 하지 못한다. 이제 사칙연산 객체를 만들어야 한다.
그런데 이러한 기능을 갖춘 객체를 만들려면 우선 a 객체에 사칙연산을 할 때 사용할 2개의 숫자를 먼저 알려주어야 한다.
다음과 같이 연산을 수행할 대상(4, 2)을 객체에 지정할 수 있게 만들어 보자.
## 객체에 숫자 지정
a.setdata(4, 2)
class FourCal:
def setdata(self, first, second): ← 매서드의 매개변수
sele.first = first ← 매서드의 수행문
self.second = second ← 매서드의 수행문
...
앞에서 만든 FourCal 클래스에서 pass 문장을 삭제하고 그 대신 setdata 함수를 만들었다.
클래스 안에 구현된 함수는 다른 말로는 메서드(Method)라고 부른다.
앞으로 클래스 내부의 함수는 항상 메서드로 표현할 예정이니 용어를 기억해 두자.
○ setdata 메서드의 매개변수
setdata 매서드는 매개변수로 self, first, second 3개 입력값을 받는다.
그런데 일부 함수와는 달리 매서드의 첫 번째 매개변수 self는 특별한 의미를 가진다.
다음과 같이 a 객체를 만들고 a 객체를 통해 setdata 매서드를 호출해 보자.
## 메서드의 매개변수
class FourCal:
def setdata(self, first, second):
self.first = first
self.second = second
...
a = FourCal()
a.setdata(4, 2)
여기서 주의할 점은 setdata 메서드에는 self, first, second 총 3개의 매개변수가 필요한데 실제로는
a.setdata(4, 2)처럼 2개 값만 전달했다. 그 이유는 a.setdata(4, 2)처럼 호출하면 setdata 메서드의 첫 번째 매개변수
self에는 setdata메서드를 호출한 객체 a가 자동으로 전달되기 때문이다.
객체를 호출할 때 호출한 객체 자신이 전될되기 때문에 self를 사용한 것이다. self말고 다른 이름을 사용해도 상관없다.
○ 메서드의 또 다른 호출 방법
잘 사용하지는 않지만 다음과 같이 클래스를 통해 메서드를 호출하는 것도 가능하다.
## 메서드 또 다른 호출
class FourCal:
def setdata(self, first, second):
self.first = first
self.second = second
...
a = FourCal()
FourCal.setdata(a, 4, 2)
○ setdata 메서드의 수행문
a.setdata(4, 2)처럼 호출하면 setdata 메서드의 매개변수 first, second에는 각각 값 4와 2가 전달되어
setdata 메서드의 수행문은 다음과 같이 해석된다.
## setdata 메서드의 수행문1
self.first = 4
self.second = 2
self는 전달된 객체 a이므로 다시 다음과 같이 해석된다.
## setdata 메서드의 수행문2
a.first = 4
a.second = 2
a.first = 4 문장이 수행되면 a 객체에 객체변수 first가 생성되고 값 4가 저장된다.
마찬가지로 a.second = 2 문장이 수행되면 a 객체에 객체변수 second가 생성되고 값 2가 저장된다.
다음 예제를 확인해 보자.
## setdata 메서드의 수행문3
a = FourCal()
a.setdata(4, 2)
print(a.first)
→ 4
print(a.second)
→ 2
a 객체에 객체변수 first와 second가 생성되었음을 확인할 수 있다.
이번에는 다음과 같이 a, b 객체를 만들어 보자. 그리고 a, b 객체에 객체변수 first를 다음과 같이 생성한다.
## setdata 메서드의 수행문4
a = FourCal()
b = FourCal()
a.setdata(4, 2)
b.setdata(3, 7)
print(a.first)
→ 4
print(b.second)
→ 3
print(a.first)
→ 4
위의 예제와 같이 a 객체의 first 값은 b 객체의 first 값에 영향을 받지 않고 원래 값을 유지하고 있음을 확인할 수 있다.
이 예제를 통해 클래스로 만든 객체의 객체변수는 다른 객체의 객체변수에 상관없이 독립적인 값을 유지한다.
id 함수를 사용하면 객체변수가 독립적인 값을 유지한다는 점을 좀 더 명확하게 증명할 수 있다.
다음 예제를 따라 해 보자.
## setdata 메서드의 수행문5
a = FourCal()
b = FourCal()
a.setdata(4, 2)
b.setdata(3, 7)
id(a.first)
→ 140734609032200
id(b.second)
→ 140734609032168
a 객체의 first 주소 값과 b 객체의 first 주소 값이 서로 다르므로 각각 다른 곳에 그 값이 저장된다는 것을 알 수 있다.
객체 변수는 다른 객체에 영향을 받지 않는다는 것을 이해하는 것이 중요하다.
○ 더하기 기능 만들기
2개의 숫자 값을 설정해 주었으니 2개의 숫자를 더하는 기능을 방금 만든 클래스에 추가해 보자.
새롭게 추가된 것은 add 메서드이다.
## 더하기 기능 만들기
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)
print(a.add())
→ 6
a.add( )라고 호출하면 add 메서드가 호출되어 값 6이 출력될 것이다. 어떤 과정을 거쳐 값이 6이 출력되는지 알아보자.
add 메서드의 매개변수는 self이고 반환 값은 result이다.
반환 값인 result를 계산하는 부분은 "result = self.first + self.second" 부분이다.
이는 "result = a.first + a.second" 이고 "result = 4 + 2"와 같이 해석한다.
따라서, 다음과 같이 a.add( )를 호출하면 6을 돌려준다.
○ 곱하기, 빼기, 나누기 기능 만들기
이번에는 곱하기, 빼지, 나누기 기능을 할 수 있는 프로그램을 만들어 보자.
## 사칙연산 만들기
class FourCal():
def setdata(self, first, second):
self.first = first
self.second = second
def add(self):
result = self.first + self.second
return result
def mul(self):
result = self.first * self.second
return result
def sub(self):
result = self.first - self.second
return result
def div(self):
result = self.first / self.second
return result
## 사칙연산 만들기2
a = FourCal()
b = FourCal()
a.setdata(4, 2)
b.setdata(3, 8)
a.add()
→ 6
a.mul()
→ 8
a.sub()
→ 2
a.div()
→ 2
b.add()
→ 11
b.mul()
→ 24
b.sub()
→ -5
b.div()
→ 0.375
생성자(Constructor)
생성자란?
객체가 생성될 때 자동으로 호출되는 메서드를 의미한다.
객체의 초깃값을 설정해야 할 필요가 있을 때는 setdata와 같은 메서드를 호출하여 초깃값을 설정하기보다는
생성자를 구현하는 것이 안전한 방법이다.
파이썬 메서드 이름으로 __init__를 사용하면 이 메서드는 생성자가 된다. 다음과 같이 생성자를 추가해 보자.
## 생성자 만들기
class FourCal():
def __init__(self, first, second):
self.first = first
self.second = second
def setdata(self, first, second):
self.first = first
self.second = second
def add(self):
result = self.first + self.second
return result
def mul(self):
result = self.first * self.second
return result
def sub(self):
result = self.first - self.second
return result
def div(self):
result = self.first / self.second
return result
여기서 새롭게 추가된 생성자 __init__ 메서드만 따로 떼어 내서 살펴보자.
__init__ 메서드는 setdata 메서드와 이름만 다르고 모든 게 동일하다.
단, 생성자로 인식하여 객체가 생성되는 시점에 자동으로 호출되는 차이가 있다.
다음 예제를 수행해 보자.
## 생성자 응용 예제
a = FourCal()
Traceback (most recent call last):
→ TypeError: FourCal.__init__() missing 2 required positional arguments: 'first' and 'second'
a = FourCal( )을 수행할 때 생성자 __init__이 호출되어 위와 같은 오류가 발생했다.
오류가 발생한 이유는 생성자의 매개변수 first와 second에 해당하는 값이 전달되지 않았기 때문이다.
위 오류를 해결하기 위해 매개변수에 값을 전달하여 객체를 생성해야 한다.
## 생성자 응용 예제
a = FourCal(4, 2)
print(a.first)
→ 4
print(a.second)
→ 2
a.add()
→ 6
a.div()
→ 2.0
위와 같이 수행하면 __init__ 메서드의 매개변수에는 각각 "self = 생성되는 객체", "first = 4", "second = 2" 와 같이 대입된다.
그리고 객체변수를 확인하면 이상없이 잘 동작하는 것을 확인할 수 있다.
클래스의 상속
상속이란?
상속이란 '물려받다'의 의미로 클래스에도 이 개념을 적용할 수 있다.
어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있게 만드는 것이다.
이번에는 상속 개념을 사용하여 우리가 만든 FourCal 클래스에 a의 b제곱을 구할 수 있는 기능을 추가해 보자.
## 클래스 상속
class MoreFourCal(FourCal):
pass
...
a = MoreFourCal(4, 2)
a.add()
→ 6
a.mul()
→ 8
a.sub()
→ 2
a.div()
→ 2
이제 원래 목적인 a의 b제곱을 계산하는 MoreFourCal 클래스를 만들어 보자.
## 클래스 상속
class MoreFourCal(FourCal):
def pow(self):
result = self.first ** self.second
return result
a = MoreFourCal(4, 2)
a.pow()
→ 16
○ 메서드 오버라이딩
이번에는 FourCal 클래스를 다음과 같이 실행해 보자.
## 메서드 오버라이딩
a = FourCal(4, 0)
a.div()
→ ZeroDivisionError: division by zero
FourCal 클래스의 객체 a에 4와 0 값을 설정하고 div 메서드를 호출하면 4를 0으로 나누려고 하기 때문에 위의 오류가 발생한다.
하지만, 0으로 나눌 때 오류가 아닌 0을 돌려주도록 만들고 싶으면 다음과 같이 상속하는 클래스를 만든다.
## 메서드 오버라이딩
class SafeFourCal(FourCal):
def div(self):
if self.second == 0:
return 0
else:
result = self.first / self.second
return result
a = SafeFourCal(4, 0)
a.div()
→ 0
위와 같이 SafeFourCal 클래스는 FourCal 클래스에 있는 div 메서드를 동일한 이름으로 다시 작성하였다.
이렇게 상위 클래스에 있는 메서드를 동일한 이름으로 다시 만드는 것을 메서드 오버라이딩(덮어쓰기)이라고 한다.
이렇게 메서드를 오버라이딩하면 상위 클래스의 메서드 대신 오버라이딩한 메서드가 호출된다.
FourCal 클래스와는 달리 Error가 발생하지 않고 의도한 대로 0을 돌려주는것을 확인할 수 있다.
클래스 변수
객체변수는 다른 객체들에 영향받지 않고 독립적으로 그 값을 유지한다는 점을 이미 알아보았다.
이번에는 객체변수와는 성격이 다른 클래스 변수에 대해 알아보자.
다음 클래스를 작성해보자.
## 클래스 변수
class Family:
lastname = "김"
print(Family.lastname)
→ 김
Family 클래스에 선언한 lastname이 바로 클래스 변수이다.
클래스 변수는 클래스 안에 함수를 선언하는 것과 마찬가지로 클래스 안에 변수를 선언하여 생성한다.
클래스 변수는 위와 같이 '클래스 이름.클래스 변수'로 사용할 수 있다.
## 클래스 변수
a = Family()
b = Family()
print(a.lastname)
→ 김
print(b.lastname)
→ 김
만약 Family 클래스의 lastname을 변경한다면 클래스로 만든 객체의 lastname 값도 모두 변경된다.
즉, 클래스 변수는 클래스로 만든 모든 객체에 공유된다는 특징이 있다.
id 함수를 사용하면 클래스 변수가 공유된다는 사실을 증명할 수 있다.
## 클래스 변수 주소
id(Family.lastname)
→ 1939509557616
id(a.lastname)
→ 1939509557616
id(b.lastname)
→ 1939509557616
id 값이 모두 같으므로 Family.lastname, a.lastname, b.lastname은 모두 같은 메모리를 가리키고 있다.
이번 연구 주제는 Python의 클래스에 대해 알아보았다.
실무 프로그래밍을 할 때 클래스 변수보다는 객체변수를 사용하는 비율이 높다고 한다.
클래스 변수보다 객체변수가 중요하다는 점을 인지하고, 객체변수를 먼저 이해하면 도움이 될 것이다.
'연구노트 > Python' 카테고리의 다른 글
| 파이썬 패키지 (0) | 2025.04.21 |
|---|---|
| 파이썬 모듈 (0) | 2025.04.18 |
| Python 파일 읽고 쓰기 (0) | 2025.04.16 |
| Python의 함수 (0) | 2025.04.16 |
| Python의 제어문(for문) (0) | 2025.04.11 |