본격적인 Django 공부에 앞서 OOP와 클래스에 대한 개념을 간단히 복습하고자 한다.
학생 딕셔너리와, 학생 점수의 평균을 반환해주는 함수를 정의하였다.
my_student = {
'name': 'Nina Hwang',
'grades': [70, 88, 90, 99]
}
def avg(student_dict):
return sum(student_dict['grades'])/len(student_dict['grades'])
print(avg(my_student))
my_student 딕셔너리와 avg() 함수는 큰 연관성이 있으나, 따로 정의되어 그 연관성을 알아보기 힘들다. 그리고 만약 'grades'라는 키 이름을 'result' 따위로 변경한다면, avg() 함수도 변경해주어야 하는데, 만약 두 코드가 따로 위치한다면 찾아서 바꾸는 데에 불편함이 있을 것이다. 또, 평균을 낸 결과를 딕셔너리 안에 저장하고 싶다면? 아쉽게도 딕셔너리 안에서 함수를 사용하는 것은 불가능하다. 게다가 이와 같은 구조를 가진 학생 딕셔너리를 수백개를 만들고자 한다면? 으 생각만해도 끔찍하다.
이러한 여러가지 문제를 한 번에 해결할 수 있는 것이 클래스이다. 위 코드는 다음과 같이 클래스를 이용해서도 작성할 수 있다.
class Student:
def __init__(self, new_name, new_grades):
self.name = new_name
self.grades = new_grades
def average(self):
return sum(self.grades) / len(self.grades)
'self.name'에서 'name'은 변수가 아니라 self의 property이다.
average()는 더이상 함수가 아니라, method로 사용된다. 위 코드는 오브젝트의 구조를 만든것일 뿐, 각각의 객체는 따로 만들어야 한다.
student_one = Student('Nina Hwang', [70, 88, 90, 99])
student_two = Student('Chaeyeong Hwang', [100, 100, 99, 100])
print(student_one.name)
print(student_one.average())
print(student_two.name)
print(student_two.average())
class Garage:
def __init__(self):
self.cars = []
def __len__(self):
return len(self.cars)
def __getitem__(self, i):
return self.cars[i]
def __repr__(self): #when you are coding(debugger....) it is useful for you to understand what is happening
return f'<Garage {self.cars}>'
def __str__(self): #useful to print things out for the users // using repr is better than using str, if you have to use only one of them
return f'Garage with {len(self)} cars'
hyundai = Garage()
hyundai.cars.append('Grandeur')
hyundai.cars.append('Sonata')
print(len(hyundai))
print(hyundai[0])
for car in hyundai:
print(car)
__len__ 메소드를 정의하지 않는다면 len()을 사용해서 len(hyundai)를 출력했을 때 'TypeError: object of type 'Garage' has no len()'가 나타난다. 또한 __getitem__을 정의하지 않는다면 ford[0]을 출력/반환한 결과는 '#TypeError: 'Garage' object is not subscriptable'가 된다. 하지만 두 메소드들을 정의함으로써, 각각 2와 'Grandeur'라는 결과를 출력한다. 뿐만 아니라 이들이 정의되어야 for loop을 사용한 반복이 가능하다(벨로그 이전 글 참고).
다음의 코드를 보자.
class Student:
def __init__(self, name, school):
self.name = name
self.school = school
self.marks = []
def average(self):
return sum(self.marks) / len(self.marks)
class WorkingStudent:
def __init__(self, name, school, salary):
self.name = name
self.school = school
self.marks = []
self.salary = salary
def average(self):
return sum(self.marks) / len(self.marks)
Student와 WorkingStudent라는 두 개의 클래스를 정의하였는데, 딱 봐도 무언가가 단단히 잘못되었음이 느껴진다. 효율성에 미쳐버린 참된 개발자는 이렇게 중복된 코드를 두고 볼 수가 없다. 이럴 때 상속(inheritance)를 사용한다.
class Student:
def __init__(self, name, school):
self.name = name
self.school = school
self.marks = []
def average(self):
return sum(self.marks) / len(self.marks)
class WorkingStudent(Student): #much more concise!
def __init__(self, name, school, salary):
super().__init__(name, school)
self.salary = salary
Student를 부모 클래스로 두고, WorkingStudent가 Student를 상속하였다. (자바스크립트와 마찬가지로 super()을 사용한다! 하지만 훨씬 간단하다!! this도, constructor도 없다!!!) WorkingStudent 클래스는 Student의 메소드도 상속받았기 때문에, append를 통해 marks 리스트에 점수를 append하고 average를 구할 수도 있다.