딕셔너리를 이용해서 코딩하기(2): 과목별 성적 관리하기 - Dictionary Comprehension

소환인·2023년 10월 6일
0

파이썬

목록 보기
2/8
post-thumbnail

사용해야 하는 데이터의 개수가 5개, 10개만 넘어가면 일일이 관리하기도 힘들고 똑같은 코드가 반복된다. 딕셔너리를 이용해서 대량의 데이터를 관리할 수 있는데, 딕셔너리를 활용하는 방법의 하나로 '딕셔너리 컴프리헨션(Dictionary Comprehension)'을 이용해보자.

예시로 사용하는 문제는 '제로베이스 스쿨''파이썬 기초 문제풀이' 강의에서 가져왔다.

문제 정의

  • 국어, 영어, 수학, 과학, 국사 5개 과목의 점수를 입력받아 총점과 평균을 구하고, 각각의 편차를 구한다.
    - 과목별 기준점수
    국어 = 85; 영어 = 82; 수학 = 89; 과학 = 75; 국사 = 94
  • 편차를 막대그래프로 시각화한다.
    - 편차가 양수면 '+'로 표시하고, 음수면 '-'로 표시한다


위와 같은 출력화면을 얻는 것이 목표다.

자료구조를 사용하지 않은 문제풀이

코딩을 하기 전, 사용해야할 변수를 먼저 생각해보면 입력받을 5개 과목의 점수를 저장할 변수 5개, 입력받은 점수로 계산할 총점과 평균, 5개 과목의 기준 점수를 저장할 변수 5개, 각 과목별 편차를 저장할 변수 5개, 총점의 편차, 평균의 편차. 과목이 5개밖에 안 되는데도 관리해야 할 변수가 이렇게나 많다.

코드를 살펴보자.

korTarget = 85; engTarget = 82; mathTarget = 89; sciTarget = 75; histTarget = 94
totalTarget = korTarget + engTarget + mathTarget + sciTarget + histTarget
avgTarget = int(totalTarget / 5)

먼저 과목별 기준점수를 저장할 변수들을 선언하고 기준점수에 대한 총점과 평균을 구했다.

kor = int(input('국어 점수 : '))
eng = int(input('영어 점수 : '))
math = int(input('수학 점수 : '))
sci = int(input('과학 점수 : '))
hist = int(input('국사 점수 : '))

total = kor + eng + math + sci + hist
avg = int(total / 5)

5개 과목의 점수를 입력받아서 저장하고 총점과 평균을 구했다.

korErr = kor - korTarget 
engErr = eng - engTarget 
mathErr = math - mathTarget 
sciErr = sci - sciTarget 
histErr = hist - histTarget

totalErr = total - totalTarget 
avgErr = avg - avgTarget

print('-'*70)

print('총점 : {}({})'.format(total, totalErr), end=', ')
print('평균 : {}({})'.format(avg, avgErr))
print('국어 : {}({})'.format(kor, korErr), end=', ')
print('영어 : {}({})'.format(eng, engErr), end=', ')
print('수학 : {}({})'.format(math, mathErr), end=', ')
print('과학 : {}({})'.format(sci, sciErr), end=', ')
print('국사 : {}({})'.format(hist, histErr))

print('-'*70)

기준점수와 입력점수와의 차이로 편차를 구하고 문자열 포맷팅을 활용해 결과를 출력해주었다. 여기까지만 봐도 비슷한 코드가 계속 반복되고 있고, 관리할 변수가 20개가 넘는다.

if korErr >= 0:
    mark = '+'
else:
    mark = '-'
print('국어 편차 : {}({})'.format(mark*abs(korErr), korErr))

if engErr >= 0:
    mark = '+'
else:
    mark = '-'
print('영어 편차 : {}({})'.format(mark*abs(engErr), engErr))

if mathErr >= 0:
    mark = '+'
else:
    mark = '-'
print('수학 편차 : {}({})'.format(mark*abs(mathErr), mathErr))

if sciErr >= 0:
    mark = '+'
else:
    mark = '-'
print('과학 편차 : {}({})'.format(mark*abs(sciErr), sciErr))

if histErr >= 0:
    mark = '+'
else:
    mark = '-'
print('국사 편차 : {}({})'.format(mark*abs(histErr), histErr))

if totalErr >= 0:
    mark = '+'
else:
    mark = '-'
print('총점 편차 : {}({})'.format(mark*abs(totalErr), totalErr))

if avgErr >= 0:
    mark = '+'
else:
    mark = '-'
print('평균 편차 : {}({})'.format(mark*abs(avgErr), avgErr))

print('-'*70)

if조건문과 print()를 이용해 과목별 편차를 막대그래프 모양으로 출력했다.
겨우 과목이 5개밖에 안 되고 문제가 복잡한 것도 아닌데 사용하는 변수가 너무 많고, 코드의 반복이 많아 가독성이 좋지 않다. 일반적인 고등학교처럼 과목이 10개가 넘어간다면 코딩양이 어마어마하게 늘어나게 된다. 과목 1개를 추가할 때마다 비슷한 코드를 반복해야 한다.

딕셔너리를 이용한 풀이

subjects = ['국어', '영어', '수학', '과학', '국사']
scores = [85, 82, 89, 75, 94]
targets = dict(zip(subjects, scores))

totalTarget = sum(scores)
avgTarget = int(totalTarget / len(subjects))

과목명을 리스트로 저장하고, 과목별 기준점수도 리스트로 저장했다. zip() 함수를 사용해 과목명과 기준점수를 튜플 형태로 묶어주고, 그걸 다시 dict()를 이용해 딕셔너리로 변환했다. 리스트로 관리되는 데이터들을 (키-밸류) 쌍으로 묶어 딕셔너리로 변환하기 위해 흔히 사용되는 방법이다. tartets 딕셔너리에 과목별 기준점수를 저장했다.

inputs = {subject:int(input(f'{subject} 점수 : ')) for subject in subjects}

total = sum(inputs.values())
avg = int(total / len(inputs))

이번 포스팅의 주제인 '딕셔너리 컴프리헨션'을 사용해 과목별 점수를 입력받았다. 형태를 보면 많이 사용하는 '리스트 컴프리헨션'과 유사하다. 기본적으로는 아래와 같은 형태로 사용된다.

{Key:Value for 요소 in 반복가능 객체(Iterable Object) [if 조건식]}

다시 문제로 돌아가서 이번엔 편차를 구해보자.

errors = dict.fromkeys(subjects, 0)

for subject, score in targets.items():
    error = inputs[subject] - score
    errors[subject] = error

errors['총점'] = total - totalTarget
errors['평균'] = avg - avgTarget

이전 포스팅 에서 사용했던 .fromkeys() 메소드를 사용해 편차를 저장할 딕셔너리를 생성하고, for문을 활용해 과목별 편차를 errors 딕셔너리에 저장한다. 총점과 평균의 편차도 함께 errors 딕셔너리에 저장한다.

print('-'*70)

print('총점 : {}({})'.format(total, errors['총점']), end=', ')
print('평균 : {}({})'.format(avg, errors['평균']))

for i, (subject, score) in enumerate(inputs.items()):

    if i < len(inputs) - 1:
        print('{} : {}({})'.format(subject, score, errors[subject]), end=', ')
    else:
        print('{} : {}({})'.format(subject, score, errors[subject]))

과목별 점수, 과목별 편차를 모두 딕셔너리로 관리하고 있기 때문에 for문을 이용해 간단하게 과목별 점수와 편차를 출력할 수 있다.

print('-'*70)

for subject, error in errors.items():

    mark = '+' if error >= 0 else '-'
    print('{} 편차 : {}({})'.format(subject, mark*abs(error), error))

print('-'*70)

편차의 양수, 음수 여부에 따라 표시 기호를 판별하는 부분도 딕셔너리와 for문을 이용하면 더 간편하게 코딩할 수 있다.


딕셔너리로 과목별 점수와 편차를 관리하고 있기 때문에 과목의 추가 및 삭제도 쉽다. subject 리스트에 과목을 추가하고, scores 리스트에 기준점수만 추가하면 간단하게 과목을 추가할 수 있다.

profile
돌고돌아

0개의 댓글