{ }codelper

배우기 › Python

더 나아가기 · 22

흔한 실수와 오류 메시지

오류 메시지 읽는 법과, 어느 언어에서나 나오는 실수들: 한 칸 차이, = 과 ==, 정수 나눗셈, null, 범위 밖 번호.

어느 언어에서나 나오는 실수가 있어요.
오류 메시지를 읽는 법과 함께 알아 둬요.
아래 예제는 번호가 범위 안인지, 나누는 수가 0인지 먼저 확인한 뒤에 써요.

def get(a, i):    if 0 <= i < len(a):          # a[-1]도 막아요(Python에서는 마지막 값)        return a[i]    return None  def safe_div(x, y):    if y == 0:        return None    return x // y  n = int(input())a = list(map(int, input().split()))input()for i in map(int, input().split()):    v = get(a, i)    if v is None:                # if not v: 로 쓰면 0도 걸려요        print(f"a[{i}]: out of range")    else:        print(f"a[{i}] = {v}")x, y = map(int, input().split())q = safe_div(x, y)print(f"{x} / {y}: cannot divide" if q is None else f"{x} / {y} = {q}")print(f"average {sum(a) / n:.1f}")
stdin
5
3 1 4 1 5
3
2 7 -1
7 0
stdout
a[2] = 4
a[7]: out of range
a[-1]: out of range
7 / 0: cannot divide
average 2.8

핵심

  • 오류 메시지에서 오류 이름, 이유, 줄 번호를 찾아요.
    Python은 맨 아래 줄부터 읽어요.
  • 컴파일하는 언어(C·C++·Java·C#)는 오류가 여러 개여도 첫 번째 오류부터 고쳐요.
    뒤의 것은 첫 오류 때문에 따라 나온 경우가 많아요.
  • 번호는 0부터 n - 1까지예요.
    반복은 i < n이어야 해요.
    i <= n은 한 칸을 넘어가는 한 칸 차이(off-by-one)예요.
  • =는 넣기, ==는 비교예요.
  • 나누기 전에 0이 아닌지, 쓰기 전에 빈 값(null)이 아닌지 먼저 물어봐요.
  • 변수는 만들 때 처음 값을 줘요(int sum = 0;).

언어별로 쓰는 법

Python마지막 줄에 IndexError: list index out of range처럼 이름과 이유, 그 위에 줄 번호.
0으로 나누면 ZeroDivisionError, None으로 계산하면 TypeError, 없는 이름은 NameError.
a[-1]은 오류가 아니라 마지막 값이고, if not v:는 0도 걸러요(v is None으로 물어요)
JavaScript범위 밖 번호는 오류 없이 undefined, 7 / 0은 Infinity.
빈 값은 null과 undefined인데 undefined + 1은 NaN, null + 1은 1로 조용히 틀려요.
비교는 ===("1" == 1이 참이에요)
TypeScriptJavaScript와 같아요
Java첫 줄 ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 5, 아래 at Main.main(Main.java:12)가 줄 번호.
0으로 나누면 ArithmeticException, null은 NullPointerException(int에는 못 담아서 Integer).
처음 값이 없으면 "might not have been initialized" 컴파일 오류
C#범위 밖은 IndexOutOfRangeException, 0으로 나누면 DivideByZeroException.
빈 값은 int?.
처음 값이 없으면 컴파일 오류 CS0165
C++a[i]는 범위를 검사하지 않아 정의되지 않은 동작, a.at(i)는 std::out_of_range 예외.
정수를 0으로 나누기도 정의되지 않은 동작(흔히 죽어요).
빈 값은 std::optional.
int sum;은 쓰레기 값으로 시작해요
C배열 범위 검사가 없어 정의되지 않은 동작: 엉뚱한 값, 다른 변수 덮기, 죽기.
0으로 나누기와 처음 값 없는 지역 변수는 C++와 같아요
Craykon범위 밖은 "목록에는 0~4번만 있어요"로 멈춰요.
7 / 0은 Infinity, 수학.몫(7, 0)은 "0으로 나눌 수 없어요".
빈값 + 1은 "빈값으로는 계산할 수 없어요"(문자열과 이으면 빈값이라고 보여요), 없는 이름은 "아직 만들지 않은 이름이에요"

자주 하는 실수

i <= n으로 한 칸 넘어갔어요C · C++ · Java · C# · JavaScript · TypeScript

int sum = 0;
for (int i = 0; i <= n; i++)
    sum += a[i];

→ Java·C#은 범위 밖 예외, JavaScript는 undefined를 더해 NaN, C는 조용히 쓰레기 값을 더해요

i < n으로 써요.

== 대신 =를 썼어요C · C++ · JavaScript

if (x = 5) {
    puts("다섯");
}

→ 오류 없이 x에 5가 들어가고 늘 참이에요(gcc -Wall은 경고해 줘요)

비교는 ==.
Java·C#은 컴파일 오류, Python은 SyntaxError(3.10부터 "Maybe you meant '=='"), 크레이콘은 문법 오류로 막아 줘요.

평균이 정수로 나왔어요Java · C# · C · C++

int sum = 7, n = 2;
double avg = sum / n;

→ avg는 3.0이에요. 7 / 2가 정수 나눗셈이라 소수가 먼저 버려져요

한쪽을 실수로 바꿔요: (double)sum / n.

언어마다 다른 점
오류로 멈추는 건 오히려 고마운 일이에요.
정말 무서운 건 C·C++의 범위 밖 접근이나 JavaScript의 undefined처럼 오류 없이 틀린 값이 나오는 경우예요.

연습 문제