[Python]클래스
<클래스(Class)>
클래스(class): 똑같은 무엇인가를 계속해서 만들어 낼 수 있는 도면(like 과자 틀)
객체(object): 클래스로 만든 피조물(like 과자 틀을 사용해 만든 과자)
클래스로 만든 객체에는 중요한 특징이 있다. 바로 객체마다 고유한 성격을 가진다는 것이다. 동일한 클래스로 만든 객체들은 서로 전혀 영향을 주지 않는다.
객체와 인스턴스의 차이
클래스로 만든 객체를 인스턴스라고도 한다. 인스턴스는 특정 객체가 어떤 클래스의 객체인지를 관계 위주로 설명할 때 사용한다. 예를 들어 a = Cookie()에서 a는 객체이고, a객체는 Cookie의 인스턴스이다. 'a는 인스턴스'보다는 'a는 객체'라는 표현이 어울리며 'a는 Cookie의 객체'보다는 'a는 Cookie의 인스턴스'라는 표현이 훨씬 잘 어울린다.
클래스(Class)
>>> class FourCal:
... pass
...
>>>
위의 코드와 같이 아무 기능이 없는 클래스를 만들 수 있다.
>>> a = FourCal()
>>> type(a)
<class '__main__.FourCal'>
a = FourCal()로 a 객체를 만들고 type(a)로 a 객체가 어떤 타입인지 알아보았다. 역시 객체 a가 FourCal 클래스의 객체임을 알 수 있다.
메서드(Method)
메서드는 클래스 안에 구현된 함수이다.
>>> class FourCal:
... def setdata(self, first, second):
... self.first = first
... self.second = second
...
>>>
위의 코드를 살펴보면 FourCal 클래스 안에 setdata라는 메서드가 구현된 것을 확인할 수 있다.
setdata 메서드의 매개변수
setdata 메서드는 매개변수로 self, first, second 3개 입력값을 받는다. 그런데 일반 함수와는 달리 메서드의 첫 번째 매개변수 self는 특별한 의미를 가진다.
>>> a = FourCal()
>>> a.setdata(4, 2)
위와 같이 a객체를 만들고 a객체를 통해 setdata를 호출할 수 있다. 그런데 setdata 메서드에는 self, first, second 총 3개의 매개변수가 필요한데 실제로는 a.setdata(4, 2)처럼 2개의 값만 전달했다. 왜냐하면 setdata 메서드의 첫 번째 매개변수 self에는 setdata메서드를 호출한 객체 a가 자동으로 전달되기 때문이다.
파이썬 메서드의 첫 번째 매개변수 이름은 관례적으로 self를 사용한다. 객체를 호출한 객체 자신이 전달되기 때문에 self를 사용하는 것이다. 물론 self말고 다른 이름을 사용해도 상관없다.
메서드의 또 다른 호출방법
>>> a = FourCal()
>>> FourCal.setdata(a, 4, 2)
위와 같이 '클래스 이름.메서드' 형태로 호출할 때는 객체 a를 첫 번째 매개변수 self에 꼭 전달해 주어야 한다. 반면 다음처럼 '객체.메서드' 형태로 호출할 때는 self를 반드시 생략해서 호출해야 한다.
>>> a = FourCal()
>>> a.setdata(4, 2)
객체변수
객체변수: 객체에 생성되는 객체만의 변수
>>> a = FourCal()
>>> b = FourCal()
>>> a.setdata(4, 2)
>>> print(a.first)
4
>>> b.setdata(3, 7)
>>> print(b.first)
3
>>> print(a.first)
4
a, b객체를 만들고 a.setdata(4, 2)를 통해 a 객체의 변수 first, second가 생성되고 값이 저장된다. print(a.first)를 통해 a 객체의 객체변수 first를 확인해보았다. 또 b.setdata(3, 7)을 통해 b 객체의 변수 first. second가 생성되고 값이 저장된다. print(b.first)를 통해 b객체의 객체변수 second를 확인해보았다. 또 print(a.first)를 통해 a 객체의 first가 변하지 않고 기존 값을 유지한다는 것을 확인할 수 있다.
클래스로 만든 객체의 객체변수는 다른 객체의 객체변수에 상관없이 독립적인 값을 유지한다.
id 함수를 사용하면 객체변수가 독립적인 값을 유지한다는 점을 좀 더 명확하게 증명해 보일 수 있다.
>>> a = FourCal()
>>> b = FourCal()
>>> a.setdata(4, 2)
>>> b.setdata(3, 7)
>>> id(a.first) # a의 first 주소값을 확인
1839194944
>>> id(b.first) # b의 first 주소값을 확인
1839194928
a객체의 first 주소 값과 b객체의 first 주소 값이 서로 다르므로 각각 다른 곳에 그 값이 저장된다는 것을 알 수 있다. 객체변수는 그 객체의 고유 값을 저장할 수 있는 공간이다. 객체 변수는 다른 객체들에 영향을 받지 않고 독립적으로 그 값을 유지한다는 점을 꼭 기억해야 한다.
생성자(Constructor)
생성자: 객체가 생성될 때 자동으로 호출되는 메서드
파이썬은 메서드 이름으로 __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__메서드는 setdata 메서드와 이름만 다르고 모든 게 동일하다. 단 메서드 이름을 __init__으로 했기 때문에 생성자로 인식되어 객체가 생성되는 시점에 자동으로 호출되는 차이가 있다.
>>> a = FourCal()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() missing 2 required positional arguments: 'first' and 'second'
a=FourCal()처럼 객체를 생성할 때 생성자 __init__이 호출되어 위와 같은 오류가 발생했다. 오류가 발생한 이유는 생성자의 매개변수 first와 second에 해당하는 값이 전달되지 않았기 때문이다. 따라서 아래와 같이 first와 second에 해당하는 값을 전달하여 객체를 생성해야 한다.
>>> a = FourCal(4, 2)
>>>
클래스의 상속
상속(Inheritance): 어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있게 만드는 것
클래스를 상속하기 위해서는 다음처러머 클래스 이름 뒤 괄호 안에 상속할 클래스 이름을 넣어주면 된다.
class 클래스 이름(상속할 클래스 이름)
상속을 사용하는 이유
보통 상속은 기존 클래스를 변경하지 않고 기능을 추가하거나 기존 기능을 변경하려고 할 때 사용한다. 만약 기존 클래스가 라이브러리 형태로 제공되거나 수정이 허용되지 않는 상황이라면 상속을 사용해야 한다.
>>> class MoreFourCal(FourCal):
... def pow(self):
... result = self.first ** self.second
... return result
...
>>>
MoreFourCal클래스는 FourCal 클래스를 상속했으므로 FourCal클래스의 모든 기능을 사용할 수 있으면서도 a의 b제곱을 계산하는 pow메서드도 추가했기에 이 기능도 사용할 수 있다.
이처럼 상속은 기존 클래스는 그대로 놔둔 채 클래스의 기능을 확장시킬 때 주로 사용한다.
메서드 오버라이딩(Overriding, 덮어쓰기)
메서드 오버라이딩: 부모 클래스(상속한 클래스)에 있는 메서드를 동일한 이름으로 다시 만드는 것
>>> a = FourCal(4, 0)
>>> a.div()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
result = self.first / self.second
ZeroDivisionError: division by zero
기존의 FourCal 클래스의 객체 a에 4와 0 값을 설정하고 div 메서드를 호출하면 4를 0으로 나누려고하기 때문에 ZeroDivisionError오류가 발생한다.
>>> class SafeFourCal(FourCal):
... def div(self):
... if self.second == 0: # 나누는 값이 0인 경우 0을 리턴하도록 수정
... return 0
... else:
... return self.first / self.second
...
>>>
>>> a = SafeFourCal(4, 0)
>>> a.div()
0
그러나 위의 코드에서는 FourCal클래스를 상속하는 SafeFourCal 클래스에 div메서드를 동일한 이름으로 다시 작성하여 메서드 오버라이딩을 했다. 메서드를 오버라이딩하면 부모클래스의 메서드 대신 오버라이딩한 메서드가 호출된다.
따라서 SafeFourCal의 객체 a는 4를 0으로 나눌 때 새롭게 정의한 div 메서드가 호출되어 오류를 발생시키지 않고 0을 돌려준다.
클래스 변수
>>> class Family:
... lastname = "김"
...
>>> print(Family.lastname)
김
클래스 변수는 클래스 안에 변수를 선언하여 생성한다. 클래스 변수는 위 예와 같이 클래스 이름.클래스 변수로 사용할 수 있다.
또 다음과 같이 Family 클래스로 만든 객체를 통해서도 클래스 변수를 사용할 수 있다.
>>> a = Family()
>>> b = Family()
>>> print(a.lastname)
김
>>> print(b.lastname)
김
>>> Family.lastname = "박"
>>> print(a.lastname)
박
>>> print(b.lastname)
박
클래스 변수 값을 변경했더니 클래스로 만든 객체의 lastname 값도 모두 변경된다는 것을 확인할 수 있다.
즉 클래스 변수는 클래스로 만든 모든 객체에 공유된다는 특징이 있다.
>>> id(Family.lastname)
4480159136
>>> id(a.lastname)
4480159136
>>> id(b.lastname)
4480159136
id 함수를 사용해 확인해보니 클래스 변수의 id 값이 모두 같으므로 Family.lastname, a.lastname, b.lastname은 모두 같은 메모리를 가르키고 있고, 이는 클래스 변수가 공유된다는 것을 증명하고 있다.
<출처>
05-1 클래스
초보 개발자들에게 클래스(class)는 넘기 힘든 장벽과도 같은 존재이다. 독자들 중에도 클래스라는 단어를 처음 접하는 이들도 있을 것이다. 그러면 도대체 클래스가 무엇인지 ...
wikidocs.net