본문으로 건너뛰기

값을 찍어 보며 고치기

파이썬 목차오류와 예외레슨 45 / 59

목차

오류 메시지 없이 프로그램이 끝까지 돌았는데 답이 틀릴 때는, 중간 값을 print()로 찍어 내 예측과 실제 값이 처음 갈리는 줄을 찾아 고칩니다.

오류 메시지 읽기에서는 파이썬이 멈추며 줄 번호를 알려 주는 오류를 보았습니다. 하지만 total = total + p 대신 total = p라고 적은 코드는 문법도 맞고 없는 이름도 쓰지 않아 멈추지 않고 틀린 답만 내놓습니다. 이럴 때는 while과 추적 표에서 배운 손 추적 표로 기대한 값을 먼저 적고, 중간에 print()를 넣어 실제 값과 대조해야 버그가 숨은 줄을 짚어낼 수 있습니다.

이 단원(오류와 예외)을 이미 안다면 단원 연습 문제부터 풀고 틀린 레슨만 보세요.

12단계 중 1단계. 6000을 기대했는데 3000이 나왔습니다. 바퀴마다 예측과 실제 total을 나란히 적어 처음 갈리는 곳을 찾습니다.

1멈추지 않는 버그 — 답만 틀릴 때

버그는 파이썬이 오류 메시지를 내고 멈추는 버그와, 끝까지 돌지만 결과가 내 생각과 다른 버그 두 가지로 나뉩니다.

세 가격 1000, 2000, 3000의 합 6000을 구하려고 아래 코드를 짰습니다. 실행 결과를 먼저 예측해 보세요.

prices = [1000, 2000, 3000]
total = 0
for p in prices:
    total = p
print(total)
실행 결과 보기
실행 결과
3000

예상과 다르면 그곳이 배울 곳입니다. 오류 메시지는 없지만 기대한 6000 대신 마지막 가격 3000만 찍혔습니다. 파이썬은 적힌 대로 실행했을 뿐 우리가 합을 구하려 했다는 속마음은 알지 못합니다(규칙 1).

틀린 값 3000이 5번 줄 print(total)에서 찍혔다고 해서 5번 줄이 범인은 아닙니다. 5번 줄은 그때 total이 가리키던 값을 보여 주었을 뿐이고, 값이 잘못된 곳은 그보다 앞선 줄에 있습니다.

2값을 찍어 보며 고치는 세 단계

답이 틀린 코드를 고칠 때는 아래 세 단계를 차례대로 밟습니다.

  • ① 예측 적기: 바퀴마다 변수에 무슨 값이 들어 있어야 맞는지 손으로 먼저 적습니다.
  • ② 의심 가는 곳에 print() 넣기: 값이 바뀌는 줄 아래에 print('점검:', p, total)⁠처럼 꼬리표와 변수를 같이 찍습니다.
  • ③ 처음 갈리는 줄 찾기: 틀린 답이 찍힌 마지막 줄이 아니라, 예측과 실제가 처음으로 달라진 줄이 범인입니다.

아까 코드의 for 반복문 안에 점검용 print()를 넣어 바퀴마다 p와 total을 찍어 보겠습니다.

prices = [1000, 2000, 3000]
total = 0
for p in prices:
    total = p
    print('점검:', p, total)
print(total)
실행 결과
점검: 1000 1000
점검: 2000 2000
점검: 3000 3000
3000
바퀴꺼낸 p예측 total실제 total판정
첫 바퀴100010001000같음
두 번째 바퀴200030002000처음 갈림
세 번째 바퀴300060003000갈림이 이어짐

첫 바퀴에서는 0 + 1000과 덮어쓴 1000이 우연히 같아 버그가 숨어 있었습니다. 두 번째 바퀴에서 예측은 3000인데 실제는 2000으로 처음 갈렸습니다. 범인은 옛 total을 더하지 않고 새 p로 덮어쓴 4번 줄 total = p입니다(규칙 3).

3고치고 점검용 print() 지우기

범인인 4번 줄을 합 모으기와 세기에서 배운 total = total + p로 고칩니다. 바퀴마다 예측(1000, 3000, 6000)과 같아진 것을 확인한 뒤에는 점검용 print() 줄을 지워 원래 출력만 남깁니다.

prices = [1000, 2000, 3000]
total = 0
for p in prices:
    total = total + p
print(total)
실행 결과
6000

예제

분기 조건이 어긋난 곳 찾기

if / else 분기에서 예상과 다른 갈래로 갈 때는 if 바로 위에 print()로 비교에 쓰인 값과 조건식 결과를 찍어 봅니다. 80점 이상이면 '합격'을 찍으려 한 아래 코드의 실행 결과를 먼저 맞혀 보세요.

score = 80
print('점검:', score, score > 80)
if score > 80:
    print('합격')
else:
    print('재시험')
실행 결과 보기
실행 결과
점검: 80 False
재시험

예상과 다르면 그곳이 배울 곳입니다. 80점 이상이면 True를 기대했는데 score > 80은 False가 나왔습니다. score >= 80으로 고쳐야 합니다.

초기화 자리가 잘못된 버그 잡기

점수 [70, 85, 90]에서 80점 이상인 과목 수(2)를 세려는데 1이 나왔습니다. 바퀴마다 s와 count를 찍어 처음 갈리는 바퀴를 찾습니다.

scores = [70, 85, 90]
for s in scores:
    count = 0
    if s >= 80:
        count = count + 1
    print('점검:', s, count)
print(count)
실행 결과
점검: 70 0
점검: 85 1
점검: 90 1
1

세 번째 바퀴(s = 90)에서 예측은 2인데 실제는 1로 처음 갈렸습니다. 바퀴마다 count = 0이 먼저 실행되어 모아 둔 1을 0으로 되돌린 것이 원인입니다. count = 0을 for 위로 옮기고 점검용 print()를 지우면 2가 찍힙니다.

자주 틀리는 곳

1. 틀린 값이 찍힌 마지막 줄만 고치려 한다

틀린 생각: 마지막 print(total) 줄에서 틀린 답이 찍혔으니 마지막 줄에 문제가 있다.

왜 이렇게 생각하기 쉬운가 하면, 내 눈에 틀린 결과가 보이는 자리가 마지막 줄이기 때문입니다. 그러나 마지막 print()는 앞쪽에서 잘못 계산된 값을 그대로 보여 줄 뿐입니다.

nums = [10, 20, 30]
total = 0
for n in nums:
    total = n
print(total)
실행 결과
30

5번 줄은 그때 total이 가리키는 30을 찍었을 뿐이고, 원인은 두 번째 바퀴에서 예측(30)과 실제(20)가 처음 갈린 4번 줄 total = n입니다.

맞는 규칙: 틀린 답이 찍힌 마지막 줄이 아니라, 내 예측과 실제 값이 처음 달라진 줄을 고칩니다.

2. 예측 없이 변수 값만 줄줄이 찍는다

틀린 생각: 코드 여기저기에 print(x)만 많이 넣어 두면 어디가 틀렸는지 저절로 보인다.

왜 이렇게 생각하기 쉬운가 하면, 값을 화면에 띄워 놓으면 원인이 눈에 띌 것 같기 때문입니다. 그러나 기대하는 값을 미리 적어 두지 않고 꼬리표 없이 숫자만 찍으면 어느 줄에서 나온 무슨 값인지 알아보기 어렵습니다.

a = 10
b = 20
for x in [1, 2]:
    a = a + x
    print(a)
    print(b)
실행 결과
11
20
13
20

숫자만 네 줄 찍혀서는 어느 것이 a이고 어느 바퀴인지 한눈에 보이지 않습니다.

맞는 규칙: 찍기 전에 기대하는 값을 먼저 적고, print('점검:', x, a, b)⁠처럼 꼬리표와 변수들을 한 줄에 같이 찍습니다.

3. 버그를 고친 뒤 점검용 print()를 남겨 둔다

틀린 생각: 버그가 고쳐졌고 마지막 줄에 맞는 답이 나오니 그대로 끝내도 된다.

왜 이렇게 생각하기 쉬운가 하면, 내가 보려던 마지막 답이 맞으면 위쪽 출력은 지나치기 쉽기 때문입니다. 그러나 점검용 print()를 지우지 않으면 원래 출력에 불필요한 줄이 섞여 나옵니다.

nums = [10, 20]
total = 0
for n in nums:
    total = total + n
    print('점검:', total)
print(total)
실행 결과
점검: 10
점검: 30
30

30만 출력해야 하는데 점검: 10과 점검: 30까지 같이 찍혔습니다.

맞는 규칙: 예측과 실제가 같아진 것을 확인한 뒤에는 점검용 print()를 모두 지우고 다시 실행해 최종 출력을 확인합니다.

확인 문제

1. 다음 코드는 [2, 4, 6]의 모든 수를 곱해 48을 구하려 한 것인데 다른 값이 나옵니다. 무엇이 출력되는지 쓰고, 몇 번째 바퀴에서 예측(2, 8, 48)과 실제가 처음 갈리는지 찾아보세요.

nums = [2, 4, 6]
prod = 1
for n in nums:
    prod = prod + n
    print('점검:', n, prod)
힌트

4번 줄이 곱하기(*)가 아니라 더하기(+)로 적혀 있습니다. 첫 바퀴(n = 2)에서 예측은 1 * 2 = 2인데, 실제 1 + 2는 얼마가 되는지 계산해 보세요.

정답
실행 결과
점검: 2 3
점검: 4 7
점검: 6 13

첫 바퀴부터 갈립니다. 첫 바퀴(n = 2)에서 곱하기였다면 prod가 2가 되어야 하는데, prod = prod + n이라 1 + 2 = 3이 되었습니다. 4번 줄을 prod = prod * n으로 고쳐야 합니다.

2. 다음 코드는 리스트 words = ['사과', '바나나', '포도']⁠에서 세 글자 이상인 단어의 개수를 세어 1을 출력하려 합니다. 빈칸에 점검용 print()를 넣어 바퀴마다 단어 w와 개수 count를 '점검:' 꼬리표와 함께 찍으려면 무엇을 써야 할까요?

words = ['사과', '바나나', '포도']
count = 0
for w in words:
    if len(w) >= 3:
        count = count + 1
    ____________________
print(count)
힌트

print()의 첫 인자로 문자열 '점검:'을 주고, 쉼표 뒤에 확인할 변수 w와 count를 나란히 적습니다.

정답

print('점검:', w, count)⁠를 넣으면 됩니다. 실행하면 점검: 사과 0, 점검: 바나나 1, 점검: 포도 1이 차례로 찍혀 바퀴마다 count가 어떻게 바뀌는지 확인할 수 있습니다.

3. 다음 코드는 [5, 2, 8, 3]에서 가장 큰 값 8을 찾으려 한 것인데 3이 출력됩니다. 어디에서 잘못되었는지 찾아 고친 코드를 적어 보세요.

nums = [5, 2, 8, 3]
best = nums[0]
for n in nums:
    if n < best:
        best = n
print(best)
힌트

바퀴마다 예측한 가장 큰 값(5, 5, 8, 8)과 실제 best를 비교해 보세요. 두 번째 바퀴(n = 2)에서 2 < 5가 True가 되어 best가 2로 바뀌어 버립니다.

정답

4번 줄의 비교 방향이 거꾸로 되어 더 큰 값이 아니라 더 작은 값으로 바꾸고 있습니다. if n < best:를 if n > best:로 고치면 8이 출력됩니다.

nums = [5, 2, 8, 3]
best = nums[0]
for n in nums:
    if n > best:
        best = n
print(best)

아래 코드에 점검용 print()를 넣어 왜 합이 60이 아니라 30이 나오는지 확인하고, 버그를 고친 뒤 점검용 print()를 지워 60만 찍히게 만들어 보세요.

처음 펼 때 약 6MB를 받습니다.컴퓨터에서 해 보기

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

파이썬 목차로