Private Attribute & super()

이찬·2023년 7월 29일
0
post-thumbnail

__attribute

앞서 멤버 attribute를 직접 읽고 쓰게 하지 말고 함수(methods)를 통해서 읽고 쓰는 것이 일반적이라고 했습니다. 아예 강제적으로 attribute를 직접 읽고 쓰는 것을 막을수 있으니, 단순하게 멤버 attribute의 이름 앞에 __를 하면 됩니다. 이미 우리가 만든 Student는 모든 변수가 직접 읽고 쓰지 않도록 하고 있으니, 모든 변수에 적용해 봅니다.

class Student:
    # Class variables
    __countStudent = 0
    
    def __init__(self, givenID, givenName):
        # Instance (or Object) variables
        self.__id = givenID
        self.__name = givenName
        Student.__countStudent = Student.__countStudent + 1
        
    def setId(self, givenID):
        self.__id = givenID
        
    def getId(self):
        return self.__id
        
    def setName(self, givenName):
        self.__name = givenName
        
    def getName(self):
        return self.__name
                
    def __str__(self):
        msg = "id:{}, name:{}".format(self.__id, self.__name)
        return msg
    
    def getNumOfStudent():
        return Student.__countStudent

그리고 나서 다음의 줄을 실행하면, 에러가 납니다. 강제적으로 클래스/객체의 멤버 attiribute를 직접 읽고 쓰는 것을 막은 것입니다.

print(Student.__countStudent)

super().init

이름이 상속이 되니 마치 생물학을 이야기 하는 것 같은데, 얼추 맞습니다.

이제 우리는 프로그래머가 만든 클래스에 필요한 경우 추가적인 attribute와 methods를 집어 넣어서 새로운 클래스를 만들수 있습니다.

왜 이렇게 할까요? 현대 프로그래머에게 가장 중요한 무기는 생산성 입니다.

즉 정해진 시간에 이미 남이 만들어 놓은 좋은 코드들을 충분하게 재사용해서, 빠르고 안전한 코드를 조기에 확보하여 남보다 빠르게 서비스를 하는 겁니다.

따라서 다른 사람 혹은 내가 만든 과거의 코드에 필요한 기능을 추가만해서, 이미 만든 코드가 안전하게 돌아가기에 새로운 기능에만 집중하는 겁니다.

Student 클래스를 이제 구분하여 하나는 "재학생", 그리고 하나는 "졸업생"으로 확장해 보도록 합니다.

우리는 졸업생을 만들어 봅시다.

class GraduatedStudent(Student):
    
    def __init__(self, givenId, givenName, givenYear):
        self.__graduatedYear = givenYear
        super().__init__(givenId, givenName)
        
    def __str__(self):
        msg = super().__str__() + ", graduation:{}".format(self.__graduatedYear)
        return msg

첫째로 클래스를 정의하는 구문에 GraduatedStudent(Student)처럼 괄호를 열고, 앞서 이미 만들어져서 이제 GraduatedStudent 클래스가 개선할 클래스의 이름을 넣습니다.

통상 이렇게 확장의 대상이 되는 클래스를 부모 혹은 베이스(base) 클래스라고 합니다.

GraduatedStudent 클래스 처럼 base 클래스에서 확장된 클래스는 자식 혹은 유전된(derived) 클래스라고 합니다.

Derived 클래스에는 base 클래스의 모든 attribute와 methods가 고스란히 포함되어 있습니다.

둘째로 객체 attribute로 __graduatedYear이 추가된 것을 볼수 있습니다.

__는 private 형태로, 직접 변수에 읽고 쓰지 못하게 하고, 멤버 methods를 통해서 읽고 쓰도록 막은 것입니다.

셋째로 derived 클래스도 생성자를 갖는 것을 볼수 있습니다.

그런데 derived 클래스에 base 클래스의 attribute가 모두 포함되기에, 졸업생의 경우도 ID와 Name을 받아야 하지만,

이 정보는 Student 클래스 안에서 관리되므로, GraduatedStudent는 본인 보다 조상인 base 클래스 정보에 접근하기 위하여 super() 함수를 사용합니다.

super().__init__(givenId, givenName)의 의미는 GraduatedStudent의 조상인 base 클래스의 생성자를 호출하라는 의미입니다.

따라서, GraduatedStudent의 생성자는 본인이 관리하는 졸업 일자를 초기화하고, 바로 이름과 ID는 base 클래스의 객체에 전달하는 것입니다.

넷째로 derived 클래스는 base 클래스와 같은 이름의 함수를 가질수 있는데, 이 경우 derived 클래스의 함수가 호출이 되면서,

필요한 경우는 base 클래스의 함수를 부를수 있으나, 필요 없으면 derived 클래스의 함수 작업 만으로 종료합니다.

이렇게 derived 클래스가 base 클래스와 똑같은 이름의 함수를 갖는 것을 오버라이딩 이라고 합니다.

우리는 derived 클래스의 __str__() 함수 호출에서 이를 볼수 있는데, 앞서 배운 super() 함수를 써서,

문자열로 출력하기 용이한 형태로 만들어 주는 __str__() 함수에 대해서 졸업 연도의 정보는 GraduatedStudent 클래스 함수에서 채우지만,

ID와 이름에 대한 정보로 문자열을 만드는 것은 base 클래스의 __str__() 함수를 통해서 획득하는 것을 볼수 있습니다.

아래의 입력창에 GraduatedStudent 클래스를 입력하고, 다음의 두줄을 함께 실행해서 동작을 이해합니다.

student1 = GraduatedStudent(20190001, "Harry Potter", 2023)

print(student1)

포함 관계(has-a Relationship) 이해하기

모든 클래스 들간의 관계가 반드시 상속에 의한 관계는 아닙니다.

예를 들어 학과는 학생들을 포함(has)하는 것이지, 상하관계 혹은 부모/자식 관계는 아닙니다.

이럴 경우는 학과의 객체에서 학생의 객체를 멤버 attribute로 가지면 됩니다.

아래는 개념적으로 학과 클래스에서 객체를 만들때, 해당 학과에 속한 학생들이 포함되도록 하는 has-a relationship을 간단하게 표현한 예입니다.

class Department:
    def __init__(self):
        memberStudent = Student()
profile
Kyunghee univ. IE 21

0개의 댓글