바쁜 분은 이것만
- 길이가 3인 리스트의 마지막 인덱스는 2(len - 1).
lst[len(lst)]는 항상 에러 - 파일을 한 줄씩
split(",")할 때는 빈 줄·칸이 모자란 줄이 범인인 경우가 많음 →len(parts)먼저 확인 - 어느 줄인지 모르면
enumerate(f, start=...)로 줄 번호를 같이 찍고, 칸이 모자라도 괜찮다면 슬라이싱row[:3]은 에러가 안 남
IndexError: list index out of range는 "리스트에 없는 칸 번호를 꺼내려 했다"는 뜻입니다. for문 범위 실수로도 나지만, 실제로 자주 만나는 건 CSV나 텍스트 파일을 한 줄씩 읽어서 split()한 다음 parts[2]처럼 꺼낼 때예요. 파일 중간에 빈 줄 하나, 칸이 하나 빠진 줄 하나만 있어도 멈춥니다. 기본 원인을 짧게 짚고, 파일 처리에서 나는 경우를 Python 3.14로 직접 재현해 정리했습니다.
1. 기본: 마지막 인덱스는 len - 1
nums = [10, 20, 30]
print("len(nums) =", len(nums))
print("마지막 인덱스 =", len(nums) - 1, "→", nums[len(nums) - 1])
print("nums[-1] =", nums[-1])
print(nums[len(nums)])
len(nums) = 3
마지막 인덱스 = 2 → 30
nums[-1] = 30
Traceback (most recent call last):
File "~/work/p1.py", line 5, in <module>
print(nums[len(nums)])
~~~~^^^^^^^^^^^
IndexError: list index out of range
인덱스는 0부터 시작하니 값이 3개면 0, 1, 2까지만 있습니다. 마지막 값은 nums[-1]로 꺼내는 게 가장 간단해요. 같은 실수가 for문에 들어가면 이렇게 됩니다.
nums = [10, 20, 30]
for i in range(len(nums) + 1):
print(i, nums[i])
0 10
1 20
2 30
Traceback (most recent call last):
File "~/work/p2.py", line 3, in <module>
print(i, nums[i])
~~~~^^^
IndexError: list index out of range
세 줄은 잘 나오다가 i가 3이 되는 순간 멈춥니다. range(len(nums))로 고치면 되고, 인덱스와 값을 같이 쓰려면 for i, n in enumerate(nums):가 더 안전해요.
2. 빈 리스트에서 [0], [-1], pop()
tests = [
("빈 리스트[0]", lambda: [][0]),
("빈 리스트[-1]", lambda: [][-1]),
("빈 리스트.pop()", lambda: [].pop()),
]
for label, f in tests:
try:
f()
except IndexError as e:
print(f"{label:12} → IndexError: {e}")
빈 리스트[0] → IndexError: list index out of range
빈 리스트[-1] → IndexError: list index out of range
빈 리스트.pop() → IndexError: pop from empty list
검색 함수나 필터를 거친 리스트가 비어 있을 때 자주 납니다. [-1]도 빈 리스트에서는 똑같이 실패하고, pop()은 메시지가 pop from empty list로 달라요. 꺼내기 전에 if items:로 비었는지 먼저 확인하면 됩니다.
3. 반복하면서 del로 지울 때
nums = [1, 2, 2, 3]
for i in range(len(nums)):
if nums[i] == 2:
del nums[i]
Traceback (most recent call last):
File "~/work/p4.py", line 3, in <module>
if nums[i] == 2:
~~~~^^^
IndexError: list index out of range
range(len(nums))는 처음 길이 4로 0~3을 미리 정해 둡니다. 그런데 반복 중에 하나를 지우면 리스트는 3칸이 돼서, i가 3일 때 없는 칸을 읽어요. 지우는 대신 남길 값만 모아 새 리스트를 만드는 게 안전합니다.
nums = [1, 2, 2, 3]
nums = [n for n in nums if n != 2]
print(nums)
[1, 3]
4. CSV 한 줄을 split했는데 칸이 모자랄 때
여기부터가 이 글의 본론입니다. 아래 같은 members.csv가 있다고 해 볼게요. 셋째 줄은 도시가 빠졌고, 그 밑에 빈 줄이 하나 있습니다.
이름,나이,도시
철수,20,서울
영희,22
민수,25,부산
with open("members.csv", encoding="utf-8") as f:
next(f) # 머리줄 건너뛰기
for line in f:
parts = line.strip().split(",")
print(parts[0], parts[1], parts[2])
철수 20 서울
Traceback (most recent call last):
File "~/work/p6.py", line 5, in <module>
print(parts[0], parts[1], parts[2])
~~~~~^^^
IndexError: list index out of range
첫 줄은 잘 나오고 "영희,22"에서 멈췄습니다. 밑줄(~~~~~^^^)이 parts[2]를 가리키니 세 번째 칸이 없다는 걸 알 수 있어요. split()이 실제로 몇 개를 돌려주는지 찍어 보면 바로 보입니다.
print("'영희,22'.split(',') →", "영희,22".split(","))
print("''.split(',') →", "".split(","))
print("''.split() →", "".split())
'영희,22'.split(',') → ['영희', '22']
''.split(',') → ['']
''.split() → []
- 쉼표가 하나뿐이면 2칸짜리 리스트가 나옵니다.
[2]는 없어요. - 빈 줄은
strip()후 빈 문자열이 되고,split(",")하면 빈 문자열 하나가 든 리스트['']가 됩니다. 길이가 1이라[1]부터 실패해요. - 구분자 없이
split()(공백 기준)을 쓰면 빈 문자열은 빈 리스트[]가 되어[0]부터 실패합니다.
파일 맨 끝에 빈 줄이 붙어 있는 경우도 많아서, 데이터가 멀쩡해 보여도 마지막에 에러가 나기도 합니다.
5. 고치는 법 1: 길이 확인하고 줄 번호 찍기
with open("members.csv", encoding="utf-8") as f:
next(f)
for lineno, line in enumerate(f, start=2):
parts = line.strip().split(",")
if len(parts) < 3:
print(f"{lineno}번째 줄 건너뜀: {parts}")
continue
name, age, city = parts
print(name, age, city)
철수 20 서울
3번째 줄 건너뜀: ['영희', '22']
4번째 줄 건너뜀: ['']
민수 25 부산
머리줄을 next(f)로 하나 넘겼으니 start=2로 세면 파일의 실제 줄 번호와 맞습니다. 데이터가 수백 줄이면 "몇 번째 줄이 문제인지"가 찍히는 것만으로도 찾는 시간이 크게 줄어요.
6. 고치는 법 2: try/except로 문제 줄만 기록
with open("members.csv", encoding="utf-8") as f:
next(f)
for lineno, line in enumerate(f, start=2):
parts = line.strip().split(",")
try:
print(parts[0], parts[1], parts[2])
except IndexError:
print(f"{lineno}번째 줄 칸 부족: {line!r}")
철수 20 서울
3번째 줄 칸 부족: '영희,22\n'
4번째 줄 칸 부족: '\n'
민수 25 부산
{line!r}로 찍으면 줄바꿈 \n까지 보여서 빈 줄인지 아닌지 한눈에 구분돼요. 다만 except IndexError는 그 블록 안의 다른 인덱스 실수까지 조용히 삼키니, try 안에는 꺼내는 코드만 짧게 두는 게 좋습니다.
7. 고치는 법 3: 슬라이싱은 범위를 넘어도 에러가 안 난다
row = "영희,22".split(",")
print("row[2:3] =", row[2:3])
print("row[:3] =", row[:3])
print("row[5:] =", row[5:])
padded = (row + [""] * 3)[:3]
print("빈칸 채우기 →", padded)
name, age, city = padded
print(f"이름={name}, 나이={age}, 도시={city or '(없음)'}")
row[2:3] = []
row[:3] = ['영희', '22']
row[5:] = []
빈칸 채우기 → ['영희', '22', '']
이름=영희, 나이=22, 도시=(없음)
row[2]는 에러지만 row[2:3]은 빈 리스트를 돌려줍니다. 이걸 이용해 (row + [""] * 3)[:3]처럼 모자란 칸을 빈 문자열로 채우면 칸 수가 항상 3개로 맞춰져요. 빠진 값을 "없음"으로 처리해도 되는 데이터일 때 쓰면 됩니다.
참고로 인덱스 대신 언패킹으로 바로 받으면 에러 종류가 바뀝니다.
row = "영희,22".split(",")
name, age, city = row
Traceback (most recent call last):
File "~/work/p11.py", line 2, in <module>
name, age, city = row
^^^^^^^^^^^^^^^
ValueError: not enough values to unpack (expected 3, got 2)
IndexError가 아니라 ValueError이고, "3개를 기대했는데 2개"라고 개수까지 알려 줘서 오히려 원인 찾기는 쉬워요.
8. 고치는 법 4: csv 모듈 쓰기
직접 split(",")하는 대신 표준 라이브러리 csv를 쓰면 빈 줄 처리가 달라집니다.
import csv
with open("members.csv", encoding="utf-8", newline="") as f:
for row in csv.reader(f):
print(len(row), row)
3 ['이름', '나이', '도시']
3 ['철수', '20', '서울']
2 ['영희', '22']
0 []
3 ['민수', '25', '부산']
csv.reader는 빈 줄을 ['']가 아니라 빈 리스트 []로 돌려줍니다. 칸이 모자란 줄은 그대로 2칸이라 row[2]를 쓰면 여전히 IndexError가 나요. 머리줄이 있는 파일이라면 DictReader가 더 편합니다.
import csv
with open("members.csv", encoding="utf-8", newline="") as f:
for row in csv.DictReader(f):
print(row["이름"], row["나이"], row["도시"])
철수 20 서울
영희 22 None
민수 25 부산
DictReader는 빈 줄을 건너뛰고, 칸이 모자란 줄은 빠진 칸을 None으로 채워 줍니다(기본값 restval=None). 인덱스 번호 대신 열 이름으로 꺼내니 IndexError가 날 자리 자체가 없어져요. 대신 None이 들어올 수 있다는 점은 기억해 두세요.
정리
| 상황 | 고치는 법 |
|---|---|
lst[len(lst)], range(len+1) |
마지막은 len - 1 또는 lst[-1], 반복은 enumerate |
빈 리스트 [0], pop() |
if items:로 먼저 확인 |
반복 중 del |
리스트 컴프리헨션으로 새 리스트 만들기 |
| split 후 칸 부족·빈 줄 | len(parts) 확인 + enumerate(f, start=2)로 줄 번호 |
| 빠진 칸을 빈 값으로 처리해도 될 때 | (row + [""] * 3)[:3] 슬라이싱 |
| 머리줄 있는 CSV | csv.DictReader로 열 이름으로 꺼내기 |
참고: Python 공식 문서 — Built-in Exceptions(IndexError, ValueError), Built-in Types(Common Sequence Operations, str.split, list.pop), Built-in Functions(enumerate, range), csv 모듈(csv.reader, csv.DictReader restval), Tutorial 3.1.3 Lists(slicing)
