티스토리 뷰
객체지향 언어로서 파이썬은 상속(inheritance), 다형성(polymorphism), 캡슐화(encapsultion) 등과 같은 기능을 제공한다. 파이썬으로 원하는 작업을 수행하기 위해 새로운 클래스를 작성하고, 새로 작성한 클래스들이 인터페이스와 계층 구조를 통해 상호작용하는 방식을 정의해야 한다.
파이썬 내장 딕셔너리 타입을 사용하면 객체 생명 주기 동안 동적인 내부 상태를 유지할 수 있다. 동적(dynamic)은 어떤 값이 들어 올지 미리 알 수 없는 식별자들을 유지해야 한다는 뜻이다.
예를 들어, 학생들의 점수를 기록해야 하는데 학생의 이름은 미리 알 수 없는 상황이라고 하면 학생별로 미리 정의된 애트리뷰트를 사용하는 대신 딕셔너리에 이름을 저장하는 클래스를 정의할 수 있다.
class SimpleGradebook:
def __init__(self):
self._grades = {}
def add_student(self, name):
self._grades[name] = []
def report_grade(self, name, score):
self._grades[name].append(score)
def average_grade(self, name):
grades = self._grades[name]
return sum(grades) / len(grades)
book = SimpleGradebook()
book.add_student('아이작 뉴턴')
book.report_grade('아이작 뉴턴', 90)
book.report_grade('아이작 뉴턴', 95)
book.report_grade('아이작 뉴턴', 85)
print(book.average_grade('아이작 뉴턴'))
>>>
90.0
예를 들어, 과목별 성적을 리스트로 저장하고 싶다고 하면, _grades 딕셔너리를 변경해서 학생 이름(키)이 아닌 다른 딕셔너리(값)으로 매핑하게 하고, 이 딕셔너리가 다시 과목(키)을 성적의 리스트(값)에 매핑하게 함으로써 과목별 성적을 구현할 수 있다.
다음 코드는 내부 딕셔너리로 defaultdict의 인스턴스를 사용해서 과목이 없는 경우를 처리한다(
from collections import defaultdict
class BySubjectGradebook:
def __init__(self):
self._Grades = {} # 외부 dict
def add_student(self, name):
self._grades[name] = defaultdict(list) # 내부 dict
다단계 딕셔너리를 처리해야 하므로 메서드가 많이 복잡해지지만, 복잡도를 관리할 수 있다.
class BySubjectGradebook:
def __init__(self):
self._grades = {} # 외부 dict
def add_student(self, name):
self._grades[name] = defaultdict(list) # 내부 dict
def report_grade(self, name, subject, grade):
by_subject = self._grades[name]
grade_list = by_subject[subject]
grade_list.append(grade)
def average_grade(self, name):
by_subject = self._grades[name]
total, count = 0, 0
for grades in by_subject.values():
total += sum(grades)
count += len(grades)
return total / count
book = BySubjectGradebook()
book.add_student('알버트 아인슈타인')
book.report_grade('알버트 아인슈타인', '수학', 75)
book.report_grade('알버트 아인슈타인', '수학', 65)
book.report_grade('알버트 아인슈타인', '체육', 90)
book.report_grade('알버트 아인슈타인', '체육', 95)
print(book.average_grade('알버트 아인슈타인'))
다시 요구 사항을 더 추가하여, 각 점수의 가중치를 함꼐 저장해서 중간고사와 기말고사가 다른 쪽지 시험보다 성적에 더 큰 영향을 미치게 하고 싶다.
이런 기능을 구현하는 한 가지 방법은 가장 안쪽에 있는 딕셔너리가 과목을 성적의 리스트로 매핑하던 것을 (성적, 가중치) 튜플의 리스트로 매핑하도록 변경하는 것이다.
class WeightedGradebook:
def __init__(self):
self._grades = {}
def add_student(self, name):
self._grades[name] = defaultdict(list)
def report_grade(self, name, subject, score, weight):
by_subject = self._grades[name]
grade_list = by_subject[subject]
grade_list.append((score, weight))
def average_grade(self, name):
by_subject = self._grades[name]
score_sum, score_count = 0, 0
for subject, scores in by_subject.items():
subject_avg, total_weight = 0, 0
for score, weight in scores:
subject_avg += score * weight
total_weight += weight
score_sum += subject_avg / total_weight
score_count += 1
return score_sum / score_count
book = WeightedGradebook()
book.add_student('알버트 아인슈타인')
book.report_grade('알버트 아인슈타인', '수학', 75, 0.05)
book.report_grade('알버트 아인슈타인', '수학', 65, 0.15)
book.report_grade('알버트 아인슈타인', '수학', 70, 0.80)
book.report_grade('알버트 아인슈타인', '체육', 100, 0.40)
book.report_grade('알버트 아인슈타인', '체육', 85, 0.60)
print(book.average_grade('알버트 아인슈타인'))
report_grade의 메서드는 리스트가 아닌 튜플 인스턴스가 들어가도록 변경되었다. 하지만 변경된 average_grade 메서드는 루프 안에 루프가 쓰이면서 복잡해졌다.
이렇게 여러 내장 타입을 사용하여 구현하게 되면, 클래스를 사용 시에 의미를 한 눈에 파악하기 힘들고 어떤 인자를 어떤 위치에 넣어야 하는지 헷갈리게 된다.
이럴 때 클래스 계층 구조를 사용해야 한다. 내포 단계가 두 단계 이상이 되면 더 이상 딕셔너리, 리스트, 튜플 계층을 사용하지 않아야 하고, 유지 보수가 어렵다. 코드에서 값을 관리하는 부분이 복잡해진다면 해당 기능을 클래스로 분리하는 것이 좋다. 데이터를 더 잘 캡슐화 해주는 잘 정의된 인터페이스를 제공하는 것이다.
grades = []
grades.append((95, 0.45))
grades.append((85, 0.55))
total = sum(score * weight for score, weight in grades)
total_weight = sum(weight for _, weight in grades)
average_grade = total / total_weight
이 코드의 문제점은 튜플에 저장된 내부 원소에 위치를 사용해 접근한다는 것이다. 만약 점수와 연관시킬 정보가 더 늘어난다면 처리하던 코드 각 부분을 모두 원소가 세 개인 튜플을 제대로 처리하도록 바꿔야 한다.
grades = []
grades.append((95, 0.45, '참 잘했어요'))
grades.append((85, 0.55, '조금 만 더 열심히'))
total = sum(score * weight for score, weight, _ in grades)
total_weight = sum(weight for _, weight, _ in grades)
average_grade = total / total_weight
원소가 세 개 이상인 튜플을 사용하면 다른 접근 방법을 고려해보아야 하고, 이런 경우 collection 모듈에 있는 namedtuple 타입이 적절하다. namedtuple을 통해 작은 불변 데이터 클래스를 쉽게 정의할 수 있다.
from collections import namedtuple
Grade = namedtuple('Grade', ('score', 'weight'))
class Subject:
def __init__(self):
self._grades = []
def report_grade(self, score, weight):
self._grades.append(Grade(score, weight))
def average_grade(self):
total, total_weight = 0, 0
for grade in self._grades:
total += grade.score * grade.weight
total_weight += grade.weight
return total / total_weight
다음으로 한 학생이 수강하는 과목을 표현하는 클래스도 작성할 수 있다.
class Student:
def __init__(self):
self._subjects = defaultdict(Subject)
def get_subject(self, name):
return self._subjects[name]
def average_grade(self):
total, count = 0, 0
for subject in self._subjects.values():
total += subject.average_grade()
count += 1
return total / count
마지막으로 모든 학생을 저장하는 컨테이너를 만들 수 있다. 이떄 학생 이름을 사용해 동적으로 학생 정보를 저장한다.
class Gradebook:
def __init__(self):
self._students = defaultdict(Student)
def get_student(self, name):
return self._students[name]
book = Gradebook()
albert = book.get_student('알버트 아인슈타인')
math = albert.get_subject('수학')
math.report_grade(75, 0.05)
math.report_grade(65, 0.15)
math.report_grade(70, 0.80)
gym = albert.get_subject('체육')
gym.report_grade(100, 0.40)
gym.report_grade(85, 0.60)
print(albert.average_grade())
이렇게 되면 결론적으로 학생 이름을 먼저 저장한 다음, 과목, 점수, 가중치 정보를 저장할 수 있다. 새로운 정보에 해당하는 학생 > 과목 > (점수, 가중치) 정보를 계층적으로 사용하는 구조가 완성된 것이다.
- 딕셔너리, 긴 튜플, 다른 내장 타입이 복잡하게 내포된 데이터를 값으로 사용하는 딕셔너리를 만들지 말 것
- 완전한 클래스가 제공하는 유연성이 필요하지 않고 가벼운 불변 데이터 컨테이너가 필요하다면 namedtuple을 활용할 것
- 내부 상태를 표현하는 딕셔너리가 복잡해지면 이 데이터를 관리하는 코드를 여러 클래스로 나눠서 재작성 할 것
'Skills > Pythons' 카테고리의 다른 글
파이썬 버전 확인 및 인터프리터 제어 모듈 sys (0) | 2024.11.04 |
---|---|
[Error] findfont: font family 'nanumgothiccoding' not found. - matplotlib 한글 폰트 나오게 하는 법 (2) | 2023.12.01 |
[Python] Python Pillow (PIL) version (0) | 2023.10.06 |
[Python] warnings 경고 메시지 숨기는 방법 (8) | 2023.07.27 |
[백준] 입출력과 사칙연산 답 모음 (0) | 2023.07.08 |
- Total
- Today
- Yesterday
- few-shot learning
- python
- prompt learning
- 도커 컨테이너
- style transfer
- 데이터셋다운로드
- 도커
- 구글드라이브서버다운
- 퓨샷러닝
- 파이썬 클래스 다형성
- 파이썬
- vscode 자동 저장
- NLP
- cs231n
- support set
- Prompt
- 구글드라이브다운
- 서버구글드라이브연동
- clip
- docker
- 파이썬 클래스 계층 구조
- 딥러닝
- stylegan
- 프롬프트
- 서버에다운
- 파이썬 딕셔너리
- CNN
- Unsupervised learning
- 구글드라이브연동
- 구글드라이브서버연동
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |