C/C++과 Java에서는 자기 자신을 가리키기 위한 포인터로 this를 사용했다. 특히, 생성자를 정의할 때 this 포인터를 자주 썼을 것이고, 취향에 따라 생략하는 경우도 많았을 것이다. 파이썬에서는 this 대신 self를 사용하는데, self는 this와는 다르게 생략할 수 없으며, 클래스 매개변수에 첫 번째 값으로 반드시 넣어주어야 한다. 또한 멤버 변수를 따로 선언할 필요 없이, 멤버 함수에서 바로 사용하면 된다.
>>> class FourCal:
... def setdata(self, first, second):
... self.first = first
... self.second = second
물론, 메서드를 정의할 때만 self가 포함될 뿐, 메서드를 사용할 때에 입력인자로 넣어주어야 하는 것은 아니다. 참고로, self가 아닌 다른 이름을 사용하는 것도 가능은 하나, 관례적으로 self라고 붙이기 때문에 self라고 명명하는 것이 좋다.
>>> a = FourCal()
>>> a.setdata(4, 2)
지금까지의 과정을 그림으로 나타내면 아래와 같다.
① 객체 생성하기
>>> a = FourCal()
② 멤버 함수, 멤버변수에 접근하기
>>> a.setdata(4, 2)
>>> a.first
4
>>> a.second
2
아래의 예시는 두개의 값을 멤버 변수에 세팅하면, 사칙 연산을 수행할 수 있는 클래스이다.
>>> 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
하지만, 현재 상태에서는 setdata 메서드를 호출하지 않으면, 아무런 메서드도 실행되지 않는다. 이러한 코드는 좋은 코드가 아니다. 잘 알다시피, setdata를 가장 먼저 호출해주기를 기대하기 보다, 생성자에서 초기 값 설정을 강제하는 편이 안전한 방법이다. 생성자는 __init__으로 정의할 수 있다.
>>> class FourCal:
... def __init__(self, first, second):
... self.first = first
... self.second = second
생성자를 생략하면, 아무런 입력인자가 없는 디폴트 생성자를 만들어주지만, 위와 같이 생성자를 하나라도 정의하면, 디폴트 생성자를 만들지 않는다. 따라서 이제부터는 first와 second에 입력 값이 없는 객체는 생성할 수 없도록 제한된다.
>>> a = FourCal() // 에러
>>> a = FourCal(4, 2)
클래스를 상속하기 위해선 아래와 같이 클래스 이름 뒤 괄호 안에 상속할 클래스의 이름을 넣어주면 된다.
>>> class MoreFourCal(FourCal):
... pass
자식 클래스는 부모 클래스의 모든 메서드를 사용할 수 있게 된다. C/C++이나 Java에서는 클래스의 멤버 함수나 멤버 변수에 접근 제어자를 지정하기 때문에, public이나 protected로 선언된 멤버에 대해서만 접근 가능했지만, 파이썬에는 접근제어자가 없기 때문에 자식은 부모 클래스의 모든 기능을 다 이용할 수 있다.
MoreFourCal이라는 클래스를 만들어보자. 이 클래스는 사칙연산 클래스인 FourCal을 상속하고 있으며, 거기에 두 수의 거듭 제곱을 계산하는 메서드가 추가로 정의된다.
>>> class MoreFourCal(FourCal):
... def pow(self):
... result = self.first ** self.second
... return result
>>> a = MoreFourCal(4, 2)
>>> a.pow()
16
>>> a.add()
6
메서드 오버라이딩에 대한 설명은 생략하고, 사용방법에 대해서만 알아보자. 부모 클래스를 상속 받은 후에 같은 이름의 메서드를 재정의하면 된다. 아래의 예시에서는 FourCal의 나눗셈 연산 메서드를 오버라이딩하여, second에 0이 들어와도 ZeroDivisionError가 발생하지 않도록 예외를 처리하고 있다.
>>> class SafeFourCal(FourCal):
... def div(self):
... if self.second == 0: # 나누는 값이 0인 경우 0을 리턴하도록 수정
... return 0
... else:
... return self.first / self.second
클래스 변수의 개념은 C/C++, Java에는 없는 개념이다. 클래스 변수와 멤버 변수(객체 변수)를 비교해보면, 멤버 변수는 서로 다른 객체에 영향을 주지 않지만, 클래스 변수는 해당 클래스로 생성된 모든 객체에 영향을 미친다.
① 클래스 변수 정의하기
>>> class Family:
... lastname = "김"
② 클래스 변수에 접근하기
>>> Family.lastname
김
>>> a = Family()
>>> b = Family()
>>> a.lastname
김
>>> b.lastname
김
③ 클래스 변수 값 변경하기
>>> Family.lastname = "박"
>>> a.lastname
박
>>> b.lastname
박
>>> a.lastname = "최"
>>> a.lastname
최
>>> Family.lastname
박
>>> b.lastname
박
※ 멤버 변수와 객체 변수
C/C++, Java에서는 보통 멤버 변수라고 부르지만, Python에서는 멤버 변수라는 표현을 잘 쓰지 않고 보통 객체 변수 또는 인스턴스 변수라고 부른다. 그 이유는 바로 위와 같이 객체마다 가지고 있는 객체 변수가 다를 수도 있기 때문이다. 멤버 변수는 모든 객체가 공유하는 변수를 가리키기 때문에, 다소 적절치 못한 네이밍일 수 있다. 다만, 클래스 변수를 사용할 일이 그렇게 많지 않고, 일반적인 경우 객체들은 대부분 같은 객체 변수를 공유하기 때문에 편의상 멤버 변수라고 부르는 경우도 있다.